Merge tag 'drm-misc-next-2019-01-23' of git://anongit.freedesktop.org/drm/drm-misc...
authorDave Airlie <airlied@redhat.com>
Thu, 24 Jan 2019 09:52:46 +0000 (19:52 +1000)
committerDave Airlie <airlied@redhat.com>
Thu, 24 Jan 2019 10:02:12 +0000 (20:02 +1000)
drm-misc-next for 5.1:

UAPI Changes:
 - Addition of the Allwinner tiled format modifier

Cross-subsystem Changes:

Core Changes:
 - dma-buf documentation improvements
 - Removal of now unused fbdev helpers
 - Addition of new drm fbdev helpers
 - Improvements to tinydrm
 - Addition of new drm_fourcc helpers
 - Impromevents to i2c-over-aux to handle I2C_M_STOP

Driver Changes:
 - Add support for the TI DS90C185 LVDS bridge
 - Improvements to the thc63lvdm83d bridge
 - Improvements to sun4i YUV and scaler support
 - Fix to the powerdown sequence of panel-innolux

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maxime Ripard <maxime.ripard@bootlin.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190123110317.h4tovujaydo2bfz2@flea
39 files changed:
Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
Documentation/devicetree/bindings/display/bridge/thine,thc63lvdm83d.txt
Documentation/devicetree/bindings/display/bridge/ti,ds90c185.txt [new file with mode: 0644]
Documentation/gpu/todo.rst
drivers/gpu/drm/Kconfig
drivers/gpu/drm/bridge/lvds-encoder.c
drivers/gpu/drm/drm_damage_helper.c
drivers/gpu/drm/drm_dp_helper.c
drivers/gpu/drm/drm_fb_cma_helper.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_gem_framebuffer_helper.c
drivers/gpu/drm/panel/panel-innolux-p079zca.c
drivers/gpu/drm/stm/ltdc.c
drivers/gpu/drm/sun4i/sun4i_backend.c
drivers/gpu/drm/sun4i/sun4i_drv.c
drivers/gpu/drm/sun4i/sun4i_frontend.c
drivers/gpu/drm/sun4i/sun4i_frontend.h
drivers/gpu/drm/sun4i/sun4i_layer.c
drivers/gpu/drm/tinydrm/core/tinydrm-core.c
drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
drivers/gpu/drm/tinydrm/hx8357d.c
drivers/gpu/drm/tinydrm/ili9225.c
drivers/gpu/drm/tinydrm/ili9341.c
drivers/gpu/drm/tinydrm/mi0283qt.c
drivers/gpu/drm/tinydrm/mipi-dbi.c
drivers/gpu/drm/tinydrm/repaper.c
drivers/gpu/drm/tinydrm/st7586.c
drivers/gpu/drm/tinydrm/st7735r.c
include/drm/drm_damage_helper.h
include/drm/drm_fb_cma_helper.h
include/drm/drm_fourcc.h
include/drm/drm_gem_framebuffer_helper.h
include/drm/drm_util.h
include/drm/tinydrm/mipi-dbi.h
include/drm/tinydrm/tinydrm-helpers.h
include/drm/tinydrm/tinydrm.h
include/linux/dma-fence-array.h
include/uapi/drm/drm_fourcc.h

index 50220190c20330a47a1425e2e2798e59c8f1652b..60091db5dfa5218958ff35133df0e3c02b4fdc5c 100644 (file)
@@ -22,13 +22,11 @@ among others.
 
 Required properties:
 
-- compatible: Must be one or more of the following
-  - "ti,ds90c185" for the TI DS90C185 FPD-Link Serializer
-  - "lvds-encoder" for a generic LVDS encoder device
+- compatible: Must be "lvds-encoder"
 
-  When compatible with the generic version, nodes must list the
-  device-specific version corresponding to the device first
-  followed by the generic version.
+  Any encoder compatible with this generic binding, but with additional
+  properties not listed here, must list a device specific compatible first
+  followed by this generic compatible.
 
 Required nodes:
 
@@ -44,8 +42,6 @@ Example
 
 lvds-encoder {
        compatible = "lvds-encoder";
-       #address-cells = <1>;
-       #size-cells = <0>;
 
        ports {
                #address-cells = <1>;
index 527e236e9a2a126dcdd814e3b50c972a7145fe44..fee3c88e1a176e6388a49806ed65dca4471e7d42 100644 (file)
@@ -10,7 +10,7 @@ Required properties:
 
 Optional properties:
 
-- pwdn-gpios: Power down control GPIO
+- powerdown-gpios: Power down control GPIO (the /PWDN pin, active low).
 
 Required nodes:
 
diff --git a/Documentation/devicetree/bindings/display/bridge/ti,ds90c185.txt b/Documentation/devicetree/bindings/display/bridge/ti,ds90c185.txt
new file mode 100644 (file)
index 0000000..e575f99
--- /dev/null
@@ -0,0 +1,55 @@
+Texas Instruments FPD-Link (LVDS) Serializer
+--------------------------------------------
+
+The DS90C185 and DS90C187 are low-power serializers for portable
+battery-powered applications that reduces the size of the RGB
+interface between the host GPU and the display.
+
+Required properties:
+
+- compatible: Should be
+  "ti,ds90c185", "lvds-encoder"  for the TI DS90C185 FPD-Link Serializer
+  "ti,ds90c187", "lvds-encoder"  for the TI DS90C187 FPD-Link Serializer
+
+Optional properties:
+
+- powerdown-gpios: Power down control GPIO (the PDB pin, active-low)
+
+Required nodes:
+
+The devices have two video ports. Their connections are modeled using the OF
+graph bindings specified in Documentation/devicetree/bindings/graph.txt.
+
+- Video port 0 for parallel input
+- Video port 1 for LVDS output
+
+
+Example
+-------
+
+lvds-encoder {
+       compatible = "ti,ds90c185", "lvds-encoder";
+
+       powerdown-gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
+
+       ports {
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               port@0 {
+                       reg = <0>;
+
+                       lvds_enc_in: endpoint {
+                               remote-endpoint = <&lcdc_out_rgb>;
+                       };
+               };
+
+               port@1 {
+                       reg = <1>;
+
+                       lvds_enc_out: endpoint {
+                               remote-endpoint = <&lvds_panel_in>;
+                       };
+               };
+       };
+};
index 0a85dad876aecb31a8a8c5d79f470c25cdd489fb..38360ede12215e6fa0d012d5498436966b38c8c9 100644 (file)
@@ -82,30 +82,6 @@ events for atomic commits correctly. But fixing these bugs is good anyway.
 
 Contact: Daniel Vetter, respective driver maintainers
 
-Better manual-upload support for atomic
----------------------------------------
-
-This would be especially useful for tinydrm:
-
-- Add a struct drm_rect dirty_clip to drm_crtc_state. When duplicating the
-  crtc state, clear that to the max values, x/y = 0 and w/h = MAX_INT, in
-  __drm_atomic_helper_crtc_duplicate_state().
-
-- Move tinydrm_merge_clips into drm_framebuffer.c, dropping the tinydrm\_
-  prefix ofc and using drm_fb\_. drm_framebuffer.c makes sense since this
-  is a function useful to implement the fb->dirty function.
-
-- Create a new drm_fb_dirty function which does essentially what e.g.
-  mipi_dbi_fb_dirty does. You can use e.g. drm_atomic_helper_update_plane as the
-  template. But instead of doing a simple full-screen plane update, this new
-  helper also sets crtc_state->dirty_clip to the right coordinates. And of
-  course it needs to check whether the fb is actually active (and maybe where),
-  so there's some book-keeping involved. There's also some good fun involved in
-  scaling things appropriately. For that case we might simply give up and
-  declare the entire area covered by the plane as dirty.
-
-Contact: Noralf Trønnes, Daniel Vetter
-
 Fallout from atomic KMS
 -----------------------
 
@@ -459,21 +435,10 @@ those drivers as simple as possible, so lots of room for refactoring:
   one of the ideas for having a shared dsi/dbi helper, abstracting away the
   transport details more.
 
-- tinydrm_gem_cma_prime_import_sg_table should probably go into the cma
-  helpers, as a _vmapped variant (since not every driver needs the vmap).
-  And tinydrm_gem_cma_free_object could the be merged into
-  drm_gem_cma_free_object().
-
-- tinydrm_fb_create we could move into drm_simple_pipe, only need to add
-  the fb_create hook to drm_simple_pipe_funcs, which would again simplify a
-  bunch of things (since it gives you a one-stop vfunc for simple drivers).
-
 - Quick aside: The unregister devm stuff is kinda getting the lifetimes of
   a drm_device wrong. Doesn't matter, since everyone else gets it wrong
   too :-)
 
-- also rework the drm_framebuffer_funcs->dirty hook wire-up, see above.
-
 Contact: Noralf Trønnes, Daniel Vetter
 
 AMD DC Display Driver
index 4385f00e1d055583df06ae4b7be0e6a6a68adc51..bd943a71756ca81bb8fe38836ef07980a26dd324 100644 (file)
@@ -170,10 +170,6 @@ config DRM_KMS_CMA_HELPER
        bool
        depends on DRM
        select DRM_GEM_CMA_HELPER
-       select DRM_KMS_FB_HELPER
-       select FB_SYS_FILLRECT
-       select FB_SYS_COPYAREA
-       select FB_SYS_IMAGEBLIT
        help
          Choose this if you need the KMS CMA helper functions
 
index f56c92f7af7c484b90fcbac98b9177e36177b949..ae8fc597eb381b2551e8beeebdf8eb32a5e2098d 100644 (file)
 #include <drm/drm_bridge.h>
 #include <drm/drm_panel.h>
 
+#include <linux/gpio/consumer.h>
 #include <linux/of_graph.h>
 
 struct lvds_encoder {
        struct drm_bridge bridge;
        struct drm_bridge *panel_bridge;
+       struct gpio_desc *powerdown_gpio;
 };
 
 static int lvds_encoder_attach(struct drm_bridge *bridge)
@@ -28,54 +30,85 @@ static int lvds_encoder_attach(struct drm_bridge *bridge)
                                 bridge);
 }
 
+static void lvds_encoder_enable(struct drm_bridge *bridge)
+{
+       struct lvds_encoder *lvds_encoder = container_of(bridge,
+                                                        struct lvds_encoder,
+                                                        bridge);
+
+       if (lvds_encoder->powerdown_gpio)
+               gpiod_set_value_cansleep(lvds_encoder->powerdown_gpio, 0);
+}
+
+static void lvds_encoder_disable(struct drm_bridge *bridge)
+{
+       struct lvds_encoder *lvds_encoder = container_of(bridge,
+                                                        struct lvds_encoder,
+                                                        bridge);
+
+       if (lvds_encoder->powerdown_gpio)
+               gpiod_set_value_cansleep(lvds_encoder->powerdown_gpio, 1);
+}
+
 static struct drm_bridge_funcs funcs = {
        .attach = lvds_encoder_attach,
+       .enable = lvds_encoder_enable,
+       .disable = lvds_encoder_disable,
 };
 
 static int lvds_encoder_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct device_node *port;
        struct device_node *endpoint;
        struct device_node *panel_node;
        struct drm_panel *panel;
        struct lvds_encoder *lvds_encoder;
 
-       lvds_encoder = devm_kzalloc(&pdev->dev, sizeof(*lvds_encoder),
-                                   GFP_KERNEL);
+       lvds_encoder = devm_kzalloc(dev, sizeof(*lvds_encoder), GFP_KERNEL);
        if (!lvds_encoder)
                return -ENOMEM;
 
+       lvds_encoder->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown",
+                                                              GPIOD_OUT_HIGH);
+       if (IS_ERR(lvds_encoder->powerdown_gpio)) {
+               int err = PTR_ERR(lvds_encoder->powerdown_gpio);
+
+               if (err != -EPROBE_DEFER)
+                       dev_err(dev, "powerdown GPIO failure: %d\n", err);
+               return err;
+       }
+
        /* Locate the panel DT node. */
-       port = of_graph_get_port_by_id(pdev->dev.of_node, 1);
+       port = of_graph_get_port_by_id(dev->of_node, 1);
        if (!port) {
-               dev_dbg(&pdev->dev, "port 1 not found\n");
+               dev_dbg(dev, "port 1 not found\n");
                return -ENXIO;
        }
 
        endpoint = of_get_child_by_name(port, "endpoint");
        of_node_put(port);
        if (!endpoint) {
-               dev_dbg(&pdev->dev, "no endpoint for port 1\n");
+               dev_dbg(dev, "no endpoint for port 1\n");
                return -ENXIO;
        }
 
        panel_node = of_graph_get_remote_port_parent(endpoint);
        of_node_put(endpoint);
        if (!panel_node) {
-               dev_dbg(&pdev->dev, "no remote endpoint for port 1\n");
+               dev_dbg(dev, "no remote endpoint for port 1\n");
                return -ENXIO;
        }
 
        panel = of_drm_find_panel(panel_node);
        of_node_put(panel_node);
        if (IS_ERR(panel)) {
-               dev_dbg(&pdev->dev, "panel not found, deferring probe\n");
+               dev_dbg(dev, "panel not found, deferring probe\n");
                return PTR_ERR(panel);
        }
 
        lvds_encoder->panel_bridge =
-               devm_drm_panel_bridge_add(&pdev->dev,
-                                         panel, DRM_MODE_CONNECTOR_LVDS);
+               devm_drm_panel_bridge_add(dev, panel, DRM_MODE_CONNECTOR_LVDS);
        if (IS_ERR(lvds_encoder->panel_bridge))
                return PTR_ERR(lvds_encoder->panel_bridge);
 
@@ -83,7 +116,7 @@ static int lvds_encoder_probe(struct platform_device *pdev)
         * but we need a bridge attached to our of_node for our user
         * to look up.
         */
-       lvds_encoder->bridge.of_node = pdev->dev.of_node;
+       lvds_encoder->bridge.of_node = dev->of_node;
        lvds_encoder->bridge.funcs = &funcs;
        drm_bridge_add(&lvds_encoder->bridge);
 
index 31032407254d4dab631fb863f58ee5353dbfeb08..e16aa5ae00b48e0c490b7843871de6b0ee87651b 100644 (file)
@@ -333,3 +333,44 @@ drm_atomic_helper_damage_iter_next(struct drm_atomic_helper_damage_iter *iter,
        return ret;
 }
 EXPORT_SYMBOL(drm_atomic_helper_damage_iter_next);
+
+/**
+ * drm_atomic_helper_damage_merged - Merged plane damage
+ * @old_state: Old plane state for validation.
+ * @state: Plane state from which to iterate the damage clips.
+ * @rect: Returns the merged damage rectangle
+ *
+ * This function merges any valid plane damage clips into one rectangle and
+ * returns it in @rect.
+ *
+ * For details see: drm_atomic_helper_damage_iter_init() and
+ * drm_atomic_helper_damage_iter_next().
+ *
+ * Returns:
+ * True if there is valid plane damage otherwise false.
+ */
+bool drm_atomic_helper_damage_merged(const struct drm_plane_state *old_state,
+                                    struct drm_plane_state *state,
+                                    struct drm_rect *rect)
+{
+       struct drm_atomic_helper_damage_iter iter;
+       struct drm_rect clip;
+       bool valid = false;
+
+       rect->x1 = INT_MAX;
+       rect->y1 = INT_MAX;
+       rect->x2 = 0;
+       rect->y2 = 0;
+
+       drm_atomic_helper_damage_iter_init(&iter, old_state, state);
+       drm_atomic_for_each_plane_damage(&iter, &clip) {
+               rect->x1 = min(rect->x1, clip.x1);
+               rect->y1 = min(rect->y1, clip.y1);
+               rect->x2 = max(rect->x2, clip.x2);
+               rect->y2 = max(rect->y2, clip.y2);
+               valid = true;
+       }
+
+       return valid;
+}
+EXPORT_SYMBOL(drm_atomic_helper_damage_merged);
index 26835d174939982a11383b94a8562d3c790d9103..54120b6319e7489e91c6c82565dd4946d7a64acd 100644 (file)
@@ -194,11 +194,11 @@ drm_dp_dump_access(const struct drm_dp_aux *aux,
        const char *arrow = request == DP_AUX_NATIVE_READ ? "->" : "<-";
 
        if (ret > 0)
-               drm_dbg(DRM_UT_DP, "%s: 0x%05x AUX %s (ret=%3d) %*ph\n",
-                       aux->name, offset, arrow, ret, min(ret, 20), buffer);
+               DRM_DEBUG_DP("%s: 0x%05x AUX %s (ret=%3d) %*ph\n",
+                            aux->name, offset, arrow, ret, min(ret, 20), buffer);
        else
-               drm_dbg(DRM_UT_DP, "%s: 0x%05x AUX %s (ret=%3d)\n",
-                       aux->name, offset, arrow, ret);
+               DRM_DEBUG_DP("%s: 0x%05x AUX %s (ret=%3d)\n",
+                            aux->name, offset, arrow, ret);
 }
 
 /**
@@ -887,7 +887,8 @@ static void drm_dp_i2c_msg_set_request(struct drm_dp_aux_msg *msg,
 {
        msg->request = (i2c_msg->flags & I2C_M_RD) ?
                DP_AUX_I2C_READ : DP_AUX_I2C_WRITE;
-       msg->request |= DP_AUX_I2C_MOT;
+       if (!(i2c_msg->flags & I2C_M_STOP))
+               msg->request |= DP_AUX_I2C_MOT;
 }
 
 /*
index 5b516615881a0029c54a8fad9db2696f02406b80..5f8074ffe7d9d5fe7ec47c3a3dbc4847ddd0c44e 100644 (file)
  * GNU General Public License for more details.
  */
 
-#include <drm/drmP.h>
-#include <drm/drm_client.h>
-#include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
 #include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_fb_cma_helper.h>
-#include <drm/drm_print.h>
+#include <drm/drm_plane.h>
 #include <linux/module.h>
 
-struct drm_fbdev_cma {
-       struct drm_fb_helper    fb_helper;
-};
-
 /**
  * DOC: framebuffer cma helper functions
  *
@@ -39,16 +32,8 @@ struct drm_fbdev_cma {
  *
  * drm_gem_fb_create() is used in the &drm_mode_config_funcs.fb_create
  * callback function to create a cma backed framebuffer.
- *
- * An fbdev framebuffer backed by cma is also available by calling
- * drm_fb_cma_fbdev_init(). drm_fb_cma_fbdev_fini() tears it down.
  */
 
-static inline struct drm_fbdev_cma *to_fbdev_cma(struct drm_fb_helper *helper)
-{
-       return container_of(helper, struct drm_fbdev_cma, fb_helper);
-}
-
 /**
  * drm_fb_cma_get_gem_obj() - Get CMA GEM object for framebuffer
  * @fb: The framebuffer
@@ -119,121 +104,3 @@ dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb,
        return paddr;
 }
 EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr);
-
-/**
- * drm_fb_cma_fbdev_init() - Allocate and initialize fbdev emulation
- * @dev: DRM device
- * @preferred_bpp: Preferred bits per pixel for the device.
- *                 @dev->mode_config.preferred_depth is used if this is zero.
- * @max_conn_count: Maximum number of connectors.
- *                  @dev->mode_config.num_connector is used if this is zero.
- *
- * Returns:
- * Zero on success or negative error code on failure.
- */
-int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp,
-                         unsigned int max_conn_count)
-{
-       struct drm_fbdev_cma *fbdev_cma;
-
-       /* dev->fb_helper will indirectly point to fbdev_cma after this call */
-       fbdev_cma = drm_fbdev_cma_init(dev, preferred_bpp, max_conn_count);
-       return PTR_ERR_OR_ZERO(fbdev_cma);
-}
-EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init);
-
-/**
- * drm_fb_cma_fbdev_fini() - Teardown fbdev emulation
- * @dev: DRM device
- */
-void drm_fb_cma_fbdev_fini(struct drm_device *dev)
-{
-       if (dev->fb_helper)
-               drm_fbdev_cma_fini(to_fbdev_cma(dev->fb_helper));
-}
-EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_fini);
-
-static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
-       .fb_probe = drm_fb_helper_generic_probe,
-};
-
-/**
- * drm_fbdev_cma_init() - Allocate and initializes a drm_fbdev_cma struct
- * @dev: DRM device
- * @preferred_bpp: Preferred bits per pixel for the device
- * @max_conn_count: Maximum number of connectors
- *
- * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR.
- */
-struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
-       unsigned int preferred_bpp, unsigned int max_conn_count)
-{
-       struct drm_fbdev_cma *fbdev_cma;
-       struct drm_fb_helper *fb_helper;
-       int ret;
-
-       fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL);
-       if (!fbdev_cma)
-               return ERR_PTR(-ENOMEM);
-
-       fb_helper = &fbdev_cma->fb_helper;
-
-       ret = drm_client_init(dev, &fb_helper->client, "fbdev", NULL);
-       if (ret)
-               goto err_free;
-
-       ret = drm_fb_helper_fbdev_setup(dev, fb_helper, &drm_fb_cma_helper_funcs,
-                                       preferred_bpp, max_conn_count);
-       if (ret)
-               goto err_client_put;
-
-       drm_client_add(&fb_helper->client);
-
-       return fbdev_cma;
-
-err_client_put:
-       drm_client_release(&fb_helper->client);
-err_free:
-       kfree(fbdev_cma);
-
-       return ERR_PTR(ret);
-}
-EXPORT_SYMBOL_GPL(drm_fbdev_cma_init);
-
-/**
- * drm_fbdev_cma_fini() - Free drm_fbdev_cma struct
- * @fbdev_cma: The drm_fbdev_cma struct
- */
-void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma)
-{
-       drm_fb_helper_unregister_fbi(&fbdev_cma->fb_helper);
-       /* All resources have now been freed by drm_fbdev_fb_destroy() */
-}
-EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini);
-
-/**
- * drm_fbdev_cma_restore_mode() - Restores initial framebuffer mode
- * @fbdev_cma: The drm_fbdev_cma struct, may be NULL
- *
- * This function is usually called from the &drm_driver.lastclose callback.
- */
-void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma)
-{
-       if (fbdev_cma)
-               drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev_cma->fb_helper);
-}
-EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode);
-
-/**
- * drm_fbdev_cma_hotplug_event() - Poll for hotpulug events
- * @fbdev_cma: The drm_fbdev_cma struct, may be NULL
- *
- * This function is usually called from the &drm_mode_config.output_poll_changed
- * callback.
- */
-void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma)
-{
-       if (fbdev_cma)
-               drm_fb_helper_hotplug_event(&fbdev_cma->fb_helper);
-}
-EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event);
index ca706fb1d975e9faac6e05e7b8222bd2781444c7..31fcf94bf8253fce1d1f0ba621fd74526f9c5c4d 100644 (file)
@@ -3042,16 +3042,8 @@ static void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper)
 static void drm_fbdev_release(struct drm_fb_helper *fb_helper)
 {
        drm_fbdev_cleanup(fb_helper);
-
-       /*
-        * FIXME:
-        * Remove conditional when all CMA drivers have been moved over to using
-        * drm_fbdev_generic_setup().
-        */
-       if (fb_helper->client.funcs) {
-               drm_client_release(&fb_helper->client);
-               kfree(fb_helper);
-       }
+       drm_client_release(&fb_helper->client);
+       kfree(fb_helper);
 }
 
 /*
index acb466d25afc28b545240149710d425ba094e9af..65edb1ccb185f01840e2395436ab51159ee67dce 100644 (file)
@@ -17,6 +17,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_damage_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_framebuffer.h>
@@ -136,10 +137,9 @@ EXPORT_SYMBOL(drm_gem_fb_create_handle);
  * @mode_cmd: Metadata from the userspace framebuffer creation request
  * @funcs: vtable to be used for the new framebuffer object
  *
- * This can be used to set &drm_framebuffer_funcs for drivers that need the
- * &drm_framebuffer_funcs.dirty callback. Use drm_gem_fb_create() if you don't
- * need to change &drm_framebuffer_funcs.
- * The function does buffer size validation.
+ * This function can be used to set &drm_framebuffer_funcs for drivers that need
+ * custom framebuffer callbacks. Use drm_gem_fb_create() if you don't need to
+ * change &drm_framebuffer_funcs. The function does buffer size validation.
  *
  * Returns:
  * Pointer to a &drm_framebuffer on success or an error pointer on failure.
@@ -215,8 +215,8 @@ static const struct drm_framebuffer_funcs drm_gem_fb_funcs = {
  *
  * If your hardware has special alignment or pitch requirements these should be
  * checked before calling this function. The function does buffer size
- * validation. Use drm_gem_fb_create_with_funcs() if you need to set
- * &drm_framebuffer_funcs.dirty.
+ * validation. Use drm_gem_fb_create_with_dirty() if you need framebuffer
+ * flushing.
  *
  * Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
  * The ADDFB2 IOCTL calls into this callback.
@@ -233,6 +233,44 @@ drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
 }
 EXPORT_SYMBOL_GPL(drm_gem_fb_create);
 
+static const struct drm_framebuffer_funcs drm_gem_fb_funcs_dirtyfb = {
+       .destroy        = drm_gem_fb_destroy,
+       .create_handle  = drm_gem_fb_create_handle,
+       .dirty          = drm_atomic_helper_dirtyfb,
+};
+
+/**
+ * drm_gem_fb_create_with_dirty() - Helper function for the
+ *                       &drm_mode_config_funcs.fb_create callback
+ * @dev: DRM device
+ * @file: DRM file that holds the GEM handle(s) backing the framebuffer
+ * @mode_cmd: Metadata from the userspace framebuffer creation request
+ *
+ * This function creates a new framebuffer object described by
+ * &drm_mode_fb_cmd2. This description includes handles for the buffer(s)
+ * backing the framebuffer. drm_atomic_helper_dirtyfb() is used for the dirty
+ * callback giving framebuffer flushing through the atomic machinery. Use
+ * drm_gem_fb_create() if you don't need the dirty callback.
+ * The function does buffer size validation.
+ *
+ * Drivers should also call drm_plane_enable_fb_damage_clips() on all planes
+ * to enable userspace to use damage clips also with the ATOMIC IOCTL.
+ *
+ * Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
+ * The ADDFB2 IOCTL calls into this callback.
+ *
+ * Returns:
+ * Pointer to a &drm_framebuffer on success or an error pointer on failure.
+ */
+struct drm_framebuffer *
+drm_gem_fb_create_with_dirty(struct drm_device *dev, struct drm_file *file,
+                            const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       return drm_gem_fb_create_with_funcs(dev, file, mode_cmd,
+                                           &drm_gem_fb_funcs_dirtyfb);
+}
+EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_dirty);
+
 /**
  * drm_gem_fb_prepare_fb() - Prepare a GEM backed framebuffer
  * @plane: Plane
index ca4ae45dd307c6cb29b16efb99caa1c1965736a5..8e5724b63f1f80f2ef50a6b4d2ca8c4a92d7e4dd 100644 (file)
@@ -70,18 +70,12 @@ static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel)
 static int innolux_panel_disable(struct drm_panel *panel)
 {
        struct innolux_panel *innolux = to_innolux_panel(panel);
-       int err;
 
        if (!innolux->enabled)
                return 0;
 
        backlight_disable(innolux->backlight);
 
-       err = mipi_dsi_dcs_set_display_off(innolux->link);
-       if (err < 0)
-               DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
-                             err);
-
        innolux->enabled = false;
 
        return 0;
@@ -95,6 +89,11 @@ static int innolux_panel_unprepare(struct drm_panel *panel)
        if (!innolux->prepared)
                return 0;
 
+       err = mipi_dsi_dcs_set_display_off(innolux->link);
+       if (err < 0)
+               DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
+                             err);
+
        err = mipi_dsi_dcs_enter_sleep_mode(innolux->link);
        if (err < 0) {
                DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
index 61dd661aa0acbde3abfe9481c9a32f4d20c79240..a91e04139595ab38e66ae094ae6cb6d620183d47 100644 (file)
@@ -691,7 +691,7 @@ 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;
+       u32 src_w, src_h;
 
        DRM_DEBUG_DRIVER("\n");
 
@@ -699,8 +699,6 @@ static int ltdc_plane_atomic_check(struct drm_plane *plane,
                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;
 
index 9e9255ee59cd83138f5572f0d31df50aec9cc244..892197f525577a1b02bc8072c67b63624335a61a 100644 (file)
@@ -45,28 +45,6 @@ static const u32 sunxi_rgb2yuv_coef[12] = {
        0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
 };
 
-/*
- * These coefficients are taken from the A33 BSP from Allwinner.
- *
- * The first three values of each row are coded as 13-bit signed fixed-point
- * numbers, with 10 bits for the fractional part. The fourth value is a
- * constant coded as a 14-bit signed fixed-point number with 4 bits for the
- * fractional part.
- *
- * The values in table order give the following colorspace translation:
- * G = 1.164 * Y - 0.391 * U - 0.813 * V + 135
- * R = 1.164 * Y + 1.596 * V - 222
- * B = 1.164 * Y + 2.018 * U + 276
- *
- * This seems to be a conversion from Y[16:235] UV[16:240] to RGB[0:255],
- * following the BT601 spec.
- */
-static const u32 sunxi_bt601_yuv2rgb_coef[12] = {
-       0x000004a7, 0x00001e6f, 0x00001cbf, 0x00000877,
-       0x000004a7, 0x00000000, 0x00000662, 0x00003211,
-       0x000004a7, 0x00000812, 0x00000000, 0x00002eb1,
-};
-
 static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine)
 {
        int i;
@@ -245,7 +223,8 @@ static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend,
                           SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN);
 
        /* TODO: Add support for the multi-planar YUV formats */
-       if (format->num_planes == 1)
+       if (drm_format_info_is_yuv_packed(format) &&
+           drm_format_info_is_yuv_sampling_422(format))
                val |= SUN4I_BACKEND_IYUVCTL_FBFMT_PACKED_YUV422;
        else
                DRM_DEBUG_DRIVER("Unsupported YUV format (0x%x)\n", fmt);
index 9e4c375ccc96fac1edfed181f9ffe2de540ce2be..93da837194cfc3e96572441e42e5672a31f686b7 100644 (file)
@@ -97,6 +97,7 @@ static int sun4i_drv_bind(struct device *dev)
        }
 
        drm_mode_config_init(drm);
+       drm->mode_config.allow_fb_modifiers = true;
 
        ret = component_bind_all(drm->dev, drm);
        if (ret) {
index 1a7ebc45747ec9d9c498adb6a793625661eff945..e8239d4d4dd5d28ac83c90acb4cf57720c6c3057 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/clk.h>
 #include <linux/component.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
@@ -48,10 +49,38 @@ static const u32 sun4i_frontend_horz_coef[64] = {
        0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
 };
 
+/*
+ * These coefficients are taken from the A33 BSP from Allwinner.
+ *
+ * The first three values of each row are coded as 13-bit signed fixed-point
+ * numbers, with 10 bits for the fractional part. The fourth value is a
+ * constant coded as a 14-bit signed fixed-point number with 4 bits for the
+ * fractional part.
+ *
+ * The values in table order give the following colorspace translation:
+ * G = 1.164 * Y - 0.391 * U - 0.813 * V + 135
+ * R = 1.164 * Y + 1.596 * V - 222
+ * B = 1.164 * Y + 2.018 * U + 276
+ *
+ * This seems to be a conversion from Y[16:235] UV[16:240] to RGB[0:255],
+ * following the BT601 spec.
+ */
+const u32 sunxi_bt601_yuv2rgb_coef[12] = {
+       0x000004a7, 0x00001e6f, 0x00001cbf, 0x00000877,
+       0x000004a7, 0x00000000, 0x00000662, 0x00003211,
+       0x000004a7, 0x00000812, 0x00000000, 0x00002eb1,
+};
+EXPORT_SYMBOL(sunxi_bt601_yuv2rgb_coef);
+
 static void sun4i_frontend_scaler_init(struct sun4i_frontend *frontend)
 {
        int i;
 
+       if (frontend->data->has_coef_access_ctrl)
+               regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
+                                 SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL,
+                                 SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL);
+
        for (i = 0; i < 32; i++) {
                regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i),
                             sun4i_frontend_horz_coef[2 * i]);
@@ -67,9 +96,11 @@ static void sun4i_frontend_scaler_init(struct sun4i_frontend *frontend)
                             sun4i_frontend_vert_coef[i]);
        }
 
-       regmap_update_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
-                          SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL,
-                          SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL);
+       if (frontend->data->has_coef_rdy)
+               regmap_write_bits(frontend->regs,
+                                 SUN4I_FRONTEND_FRM_CTRL_REG,
+                                 SUN4I_FRONTEND_FRM_CTRL_COEF_RDY,
+                                 SUN4I_FRONTEND_FRM_CTRL_COEF_RDY);
 }
 
 int sun4i_frontend_init(struct sun4i_frontend *frontend)
@@ -84,59 +115,228 @@ void sun4i_frontend_exit(struct sun4i_frontend *frontend)
 }
 EXPORT_SYMBOL(sun4i_frontend_exit);
 
+static bool sun4i_frontend_format_chroma_requires_swap(uint32_t fmt)
+{
+       switch (fmt) {
+       case DRM_FORMAT_YVU411:
+       case DRM_FORMAT_YVU420:
+       case DRM_FORMAT_YVU422:
+       case DRM_FORMAT_YVU444:
+               return true;
+
+       default:
+               return false;
+       }
+}
+
+static bool sun4i_frontend_format_supports_tiling(uint32_t fmt)
+{
+       switch (fmt) {
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV16:
+       case DRM_FORMAT_NV21:
+       case DRM_FORMAT_NV61:
+       case DRM_FORMAT_YUV411:
+       case DRM_FORMAT_YUV420:
+       case DRM_FORMAT_YUV422:
+       case DRM_FORMAT_YVU420:
+       case DRM_FORMAT_YVU422:
+       case DRM_FORMAT_YVU411:
+               return true;
+
+       default:
+               return false;
+       }
+}
+
 void sun4i_frontend_update_buffer(struct sun4i_frontend *frontend,
                                  struct drm_plane *plane)
 {
        struct drm_plane_state *state = plane->state;
        struct drm_framebuffer *fb = state->fb;
+       unsigned int strides[3] = {};
+
        dma_addr_t paddr;
+       bool swap;
+
+       if (fb->modifier == DRM_FORMAT_MOD_ALLWINNER_TILED) {
+               unsigned int width = state->src_w >> 16;
+               unsigned int offset;
+
+               strides[0] = SUN4I_FRONTEND_LINESTRD_TILED(fb->pitches[0]);
+
+               /*
+                * The X1 offset is the offset to the bottom-right point in the
+                * end tile, which is the final pixel (at offset width - 1)
+                * within the end tile (with a 32-byte mask).
+                */
+               offset = (width - 1) & (32 - 1);
+
+               regmap_write(frontend->regs, SUN4I_FRONTEND_TB_OFF0_REG,
+                            SUN4I_FRONTEND_TB_OFF_X1(offset));
+
+               if (fb->format->num_planes > 1) {
+                       strides[1] =
+                               SUN4I_FRONTEND_LINESTRD_TILED(fb->pitches[1]);
+
+                       regmap_write(frontend->regs, SUN4I_FRONTEND_TB_OFF1_REG,
+                                    SUN4I_FRONTEND_TB_OFF_X1(offset));
+               }
+
+               if (fb->format->num_planes > 2) {
+                       strides[2] =
+                               SUN4I_FRONTEND_LINESTRD_TILED(fb->pitches[2]);
+
+                       regmap_write(frontend->regs, SUN4I_FRONTEND_TB_OFF2_REG,
+                                    SUN4I_FRONTEND_TB_OFF_X1(offset));
+               }
+       } else {
+               strides[0] = fb->pitches[0];
+
+               if (fb->format->num_planes > 1)
+                       strides[1] = fb->pitches[1];
+
+               if (fb->format->num_planes > 2)
+                       strides[2] = fb->pitches[2];
+       }
 
        /* Set the line width */
        DRM_DEBUG_DRIVER("Frontend stride: %d bytes\n", fb->pitches[0]);
        regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD0_REG,
-                    fb->pitches[0]);
+                    strides[0]);
+
+       if (fb->format->num_planes > 1)
+               regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD1_REG,
+                            strides[1]);
+
+       if (fb->format->num_planes > 2)
+               regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD2_REG,
+                            strides[2]);
+
+       /* Some planar formats require chroma channel swapping by hand. */
+       swap = sun4i_frontend_format_chroma_requires_swap(fb->format->format);
 
        /* Set the physical address of the buffer in memory */
        paddr = drm_fb_cma_get_gem_addr(fb, state, 0);
        paddr -= PHYS_OFFSET;
-       DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
+       DRM_DEBUG_DRIVER("Setting buffer #0 address to %pad\n", &paddr);
        regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR0_REG, paddr);
+
+       if (fb->format->num_planes > 1) {
+               paddr = drm_fb_cma_get_gem_addr(fb, state, swap ? 2 : 1);
+               paddr -= PHYS_OFFSET;
+               DRM_DEBUG_DRIVER("Setting buffer #1 address to %pad\n", &paddr);
+               regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR1_REG,
+                            paddr);
+       }
+
+       if (fb->format->num_planes > 2) {
+               paddr = drm_fb_cma_get_gem_addr(fb, state, swap ? 1 : 2);
+               paddr -= PHYS_OFFSET;
+               DRM_DEBUG_DRIVER("Setting buffer #2 address to %pad\n", &paddr);
+               regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR2_REG,
+                            paddr);
+       }
 }
 EXPORT_SYMBOL(sun4i_frontend_update_buffer);
 
-static int sun4i_frontend_drm_format_to_input_fmt(uint32_t fmt, u32 *val)
+static int
+sun4i_frontend_drm_format_to_input_fmt(const struct drm_format_info *format,
+                                      u32 *val)
 {
-       switch (fmt) {
-       case DRM_FORMAT_XRGB8888:
+       if (!format->is_yuv)
                *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_RGB;
-               return 0;
-
-       default:
+       else if (drm_format_info_is_yuv_sampling_411(format))
+               *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV411;
+       else if (drm_format_info_is_yuv_sampling_420(format))
+               *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV420;
+       else if (drm_format_info_is_yuv_sampling_422(format))
+               *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV422;
+       else if (drm_format_info_is_yuv_sampling_444(format))
+               *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV444;
+       else
                return -EINVAL;
-       }
+
+       return 0;
 }
 
-static int sun4i_frontend_drm_format_to_input_mode(uint32_t fmt, u32 *val)
+static int
+sun4i_frontend_drm_format_to_input_mode(const struct drm_format_info *format,
+                                       uint64_t modifier, u32 *val)
 {
-       if (drm_format_num_planes(fmt) == 1)
+       bool tiled = (modifier == DRM_FORMAT_MOD_ALLWINNER_TILED);
+
+       switch (format->num_planes) {
+       case 1:
                *val = SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PACKED;
-       else
-               return -EINVAL;
+               return 0;
 
-       return 0;
+       case 2:
+               *val = tiled ? SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_SEMIPLANAR
+                            : SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_SEMIPLANAR;
+               return 0;
+
+       case 3:
+               *val = tiled ? SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_PLANAR
+                            : SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PLANAR;
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
 }
 
-static int sun4i_frontend_drm_format_to_input_sequence(uint32_t fmt, u32 *val)
+static int
+sun4i_frontend_drm_format_to_input_sequence(const struct drm_format_info *format,
+                                           u32 *val)
 {
-       switch (fmt) {
+       /* Planar formats have an explicit input sequence. */
+       if (drm_format_info_is_yuv_planar(format)) {
+               *val = 0;
+               return 0;
+       }
+
+       switch (format->format) {
        case DRM_FORMAT_BGRX8888:
                *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_BGRX;
                return 0;
 
+       case DRM_FORMAT_NV12:
+               *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UV;
+               return 0;
+
+       case DRM_FORMAT_NV16:
+               *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UV;
+               return 0;
+
+       case DRM_FORMAT_NV21:
+               *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VU;
+               return 0;
+
+       case DRM_FORMAT_NV61:
+               *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VU;
+               return 0;
+
+       case DRM_FORMAT_UYVY:
+               *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UYVY;
+               return 0;
+
+       case DRM_FORMAT_VYUY:
+               *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VYUY;
+               return 0;
+
        case DRM_FORMAT_XRGB8888:
                *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_XRGB;
                return 0;
 
+       case DRM_FORMAT_YUYV:
+               *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YUYV;
+               return 0;
+
+       case DRM_FORMAT_YVYU:
+               *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YVYU;
+               return 0;
+
        default:
                return -EINVAL;
        }
@@ -160,14 +360,32 @@ static int sun4i_frontend_drm_format_to_output_fmt(uint32_t fmt, u32 *val)
 
 static const uint32_t sun4i_frontend_formats[] = {
        DRM_FORMAT_BGRX8888,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_NV16,
+       DRM_FORMAT_NV21,
+       DRM_FORMAT_NV61,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_VYUY,
        DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_YUV411,
+       DRM_FORMAT_YUV420,
+       DRM_FORMAT_YUV422,
+       DRM_FORMAT_YUV444,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_YVU411,
+       DRM_FORMAT_YVU420,
+       DRM_FORMAT_YVU422,
+       DRM_FORMAT_YVU444,
+       DRM_FORMAT_YVYU,
 };
 
 bool sun4i_frontend_format_is_supported(uint32_t fmt, uint64_t modifier)
 {
        unsigned int i;
 
-       if (modifier != DRM_FORMAT_MOD_LINEAR)
+       if (modifier == DRM_FORMAT_MOD_ALLWINNER_TILED)
+               return sun4i_frontend_format_supports_tiling(fmt);
+       else if (modifier != DRM_FORMAT_MOD_LINEAR)
                return false;
 
        for (i = 0; i < ARRAY_SIZE(sun4i_frontend_formats); i++)
@@ -183,9 +401,12 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend,
 {
        struct drm_plane_state *state = plane->state;
        struct drm_framebuffer *fb = state->fb;
-       uint32_t format = fb->format->format;
+       const struct drm_format_info *format = fb->format;
+       uint64_t modifier = fb->modifier;
        u32 out_fmt_val;
        u32 in_fmt_val, in_mod_val, in_ps_val;
+       unsigned int i;
+       u32 bypass;
        int ret;
 
        ret = sun4i_frontend_drm_format_to_input_fmt(format, &in_fmt_val);
@@ -194,7 +415,8 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend,
                return ret;
        }
 
-       ret = sun4i_frontend_drm_format_to_input_mode(format, &in_mod_val);
+       ret = sun4i_frontend_drm_format_to_input_mode(format, modifier,
+                                                     &in_mod_val);
        if (ret) {
                DRM_DEBUG_DRIVER("Invalid input mode\n");
                return ret;
@@ -216,16 +438,39 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend,
         * I have no idea what this does exactly, but it seems to be
         * related to the scaler FIR filter phase parameters.
         */
-       regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG, 0x400);
-       regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG, 0x400);
-       regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG, 0x400);
-       regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG, 0x400);
-       regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG, 0x400);
-       regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG, 0x400);
+       regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG,
+                    frontend->data->ch_phase[0].horzphase);
+       regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG,
+                    frontend->data->ch_phase[1].horzphase);
+       regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG,
+                    frontend->data->ch_phase[0].vertphase[0]);
+       regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG,
+                    frontend->data->ch_phase[1].vertphase[0]);
+       regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG,
+                    frontend->data->ch_phase[0].vertphase[1]);
+       regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG,
+                    frontend->data->ch_phase[1].vertphase[1]);
+
+       /*
+        * Checking the input format is sufficient since we currently only
+        * support RGB output formats to the backend. If YUV output formats
+        * ever get supported, an YUV input and output would require bypassing
+        * the CSC engine too.
+        */
+       if (format->is_yuv) {
+               /* Setup the CSC engine for YUV to RGB conversion. */
+               bypass = 0;
+
+               for (i = 0; i < ARRAY_SIZE(sunxi_bt601_yuv2rgb_coef); i++)
+                       regmap_write(frontend->regs,
+                                    SUN4I_FRONTEND_CSC_COEF_REG(i),
+                                    sunxi_bt601_yuv2rgb_coef[i]);
+       } else {
+               bypass = SUN4I_FRONTEND_BYPASS_CSC_EN;
+       }
 
        regmap_update_bits(frontend->regs, SUN4I_FRONTEND_BYPASS_REG,
-                          SUN4I_FRONTEND_BYPASS_CSC_EN,
-                          SUN4I_FRONTEND_BYPASS_CSC_EN);
+                          SUN4I_FRONTEND_BYPASS_CSC_EN, bypass);
 
        regmap_write(frontend->regs, SUN4I_FRONTEND_INPUT_FMT_REG,
                     in_mod_val | in_fmt_val | in_ps_val);
@@ -321,6 +566,10 @@ static int sun4i_frontend_bind(struct device *dev, struct device *master,
        frontend->dev = dev;
        frontend->node = dev->of_node;
 
+       frontend->data = of_device_get_match_data(dev);
+       if (!frontend->data)
+               return -ENODEV;
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        regs = devm_ioremap_resource(dev, res);
        if (IS_ERR(regs))
@@ -433,8 +682,47 @@ static const struct dev_pm_ops sun4i_frontend_pm_ops = {
        .runtime_suspend        = sun4i_frontend_runtime_suspend,
 };
 
+static const struct sun4i_frontend_data sun4i_a10_frontend = {
+       .ch_phase               = {
+               {
+                       .horzphase = 0,
+                       .vertphase = { 0, 0 },
+               },
+               {
+                       .horzphase = 0xfc000,
+                       .vertphase = { 0xfc000, 0xfc000 },
+               },
+       },
+       .has_coef_rdy           = true,
+};
+
+static const struct sun4i_frontend_data sun8i_a33_frontend = {
+       .ch_phase               = {
+               {
+                       .horzphase = 0x400,
+                       .vertphase = { 0x400, 0x400 },
+               },
+               {
+                       .horzphase = 0x400,
+                       .vertphase = { 0x400, 0x400 },
+               },
+       },
+       .has_coef_access_ctrl   = true,
+};
+
 const struct of_device_id sun4i_frontend_of_table[] = {
-       { .compatible = "allwinner,sun8i-a33-display-frontend" },
+       {
+               .compatible = "allwinner,sun4i-a10-display-frontend",
+               .data = &sun4i_a10_frontend
+       },
+       {
+               .compatible = "allwinner,sun7i-a20-display-frontend",
+               .data = &sun4i_a10_frontend
+       },
+       {
+               .compatible = "allwinner,sun8i-a33-display-frontend",
+               .data = &sun8i_a33_frontend
+       },
        { }
 };
 EXPORT_SYMBOL(sun4i_frontend_of_table);
index ad146e8d8d70d1d05e82420cd45bb0f7e4257bef..0c382c1ddb0fe81b0eb8f63999d0f6e2856d650c 100644 (file)
 #define SUN4I_FRONTEND_BYPASS_CSC_EN                   BIT(1)
 
 #define SUN4I_FRONTEND_BUF_ADDR0_REG           0x020
+#define SUN4I_FRONTEND_BUF_ADDR1_REG           0x024
+#define SUN4I_FRONTEND_BUF_ADDR2_REG           0x028
+
+#define SUN4I_FRONTEND_TB_OFF0_REG             0x030
+#define SUN4I_FRONTEND_TB_OFF1_REG             0x034
+#define SUN4I_FRONTEND_TB_OFF2_REG             0x038
+#define SUN4I_FRONTEND_TB_OFF_X1(x1)                   ((x1) << 16)
+#define SUN4I_FRONTEND_TB_OFF_Y0(y0)                   ((y0) << 8)
+#define SUN4I_FRONTEND_TB_OFF_X0(x0)                   (x0)
 
 #define SUN4I_FRONTEND_LINESTRD0_REG           0x040
+#define SUN4I_FRONTEND_LINESTRD1_REG           0x044
+#define SUN4I_FRONTEND_LINESTRD2_REG           0x048
+
+/*
+ * In tiled mode, the stride is defined as the distance between the start of the
+ * end line of the current tile and the start of the first line in the next
+ * vertical tile.
+ *
+ * Tiles are represented in row-major order, thus the end line of current tile
+ * starts at: 31 * 32 (31 lines of 32 cols), the next vertical tile starts at:
+ * 32-bit-aligned-width * 32 and the distance is:
+ * 32 * (32-bit-aligned-width - 31).
+ */
+#define SUN4I_FRONTEND_LINESTRD_TILED(stride)          (((stride) - 31) * 32)
 
 #define SUN4I_FRONTEND_INPUT_FMT_REG           0x04c
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PLANAR       (0 << 8)
 #define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PACKED       (1 << 8)
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_SEMIPLANAR   (2 << 8)
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_PLANAR  (4 << 8)
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_SEMIPLANAR (6 << 8)
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV444       (0 << 4)
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV422       (1 << 4)
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV420       (2 << 4)
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV411       (3 << 4)
 #define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_RGB          (5 << 4)
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UYVY          0
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YUYV          1
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VYUY          2
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YVYU          3
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UV            0
+#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VU            1
 #define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_BGRX          0
 #define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_XRGB          1
 
@@ -35,6 +72,8 @@
 #define SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_BGRX8888    1
 #define SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_XRGB8888    2
 
+#define SUN4I_FRONTEND_CSC_COEF_REG(c)         (0x070 + (0x4 * (c)))
+
 #define SUN4I_FRONTEND_CH0_INSIZE_REG          0x100
 #define SUN4I_FRONTEND_INSIZE(h, w)                    ((((h) - 1) << 16) | (((w) - 1)))
 
@@ -73,6 +112,16 @@ struct drm_plane;
 struct regmap;
 struct reset_control;
 
+struct sun4i_frontend_data {
+       bool    has_coef_access_ctrl;
+       bool    has_coef_rdy;
+
+       struct {
+               u32     horzphase;
+               u32     vertphase[2];
+       } ch_phase[2];
+};
+
 struct sun4i_frontend {
        struct list_head        list;
        struct device           *dev;
@@ -83,9 +132,12 @@ struct sun4i_frontend {
        struct clk              *ram_clk;
        struct regmap           *regs;
        struct reset_control    *reset;
+
+       const struct sun4i_frontend_data        *data;
 };
 
 extern const struct of_device_id sun4i_frontend_of_table[];
+extern const u32 sunxi_bt601_yuv2rgb_coef[12];
 
 int sun4i_frontend_init(struct sun4i_frontend *frontend);
 void sun4i_frontend_exit(struct sun4i_frontend *frontend);
index 29631e0efde37ce709023995634dceac8c412f6a..c5a999ca1d72a90e8debfd108fb3b13fdeec6272 100644 (file)
@@ -114,6 +114,13 @@ static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
        sun4i_backend_layer_enable(backend, layer->id, true);
 }
 
+static bool sun4i_layer_format_mod_supported(struct drm_plane *plane,
+                                            uint32_t format, uint64_t modifier)
+{
+       return sun4i_backend_format_is_supported(format, modifier) ||
+              sun4i_frontend_format_is_supported(format, modifier);
+}
+
 static const struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
        .prepare_fb     = drm_gem_fb_prepare_fb,
        .atomic_disable = sun4i_backend_layer_atomic_disable,
@@ -127,6 +134,7 @@ static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
        .disable_plane          = drm_atomic_helper_disable_plane,
        .reset                  = sun4i_backend_layer_reset,
        .update_plane           = drm_atomic_helper_update_plane,
+       .format_mod_supported   = sun4i_layer_format_mod_supported,
 };
 
 static const uint32_t sun4i_layer_formats[] = {
@@ -138,13 +146,31 @@ static const uint32_t sun4i_layer_formats[] = {
        DRM_FORMAT_RGBA4444,
        DRM_FORMAT_RGB888,
        DRM_FORMAT_RGB565,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_NV16,
+       DRM_FORMAT_NV21,
+       DRM_FORMAT_NV61,
        DRM_FORMAT_UYVY,
        DRM_FORMAT_VYUY,
        DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_YUV411,
+       DRM_FORMAT_YUV420,
+       DRM_FORMAT_YUV422,
+       DRM_FORMAT_YUV444,
        DRM_FORMAT_YUYV,
+       DRM_FORMAT_YVU411,
+       DRM_FORMAT_YVU420,
+       DRM_FORMAT_YVU422,
+       DRM_FORMAT_YVU444,
        DRM_FORMAT_YVYU,
 };
 
+static const uint64_t sun4i_layer_modifiers[] = {
+       DRM_FORMAT_MOD_LINEAR,
+       DRM_FORMAT_MOD_ALLWINNER_TILED,
+       DRM_FORMAT_MOD_INVALID
+};
+
 static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
                                                struct sun4i_backend *backend,
                                                enum drm_plane_type type)
@@ -161,7 +187,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
                                       &sun4i_backend_layer_funcs,
                                       sun4i_layer_formats,
                                       ARRAY_SIZE(sun4i_layer_formats),
-                                      NULL, type, NULL);
+                                      sun4i_layer_modifiers, type, NULL);
        if (ret) {
                dev_err(drm->dev, "Couldn't initialize layer\n");
                return ERR_PTR(ret);
index aeb93eadb04716d038cef17fc6dae674673d998a..614f532ea89f652755a9ebca76c522e89573973d 100644 (file)
  * and registers the DRM device using devm_tinydrm_register().
  */
 
-static struct drm_framebuffer *
-tinydrm_fb_create(struct drm_device *drm, struct drm_file *file_priv,
-                 const struct drm_mode_fb_cmd2 *mode_cmd)
-{
-       struct tinydrm_device *tdev = drm->dev_private;
-
-       return drm_gem_fb_create_with_funcs(drm, file_priv, mode_cmd,
-                                           tdev->fb_funcs);
-}
-
 static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = {
-       .fb_create = tinydrm_fb_create,
+       .fb_create = drm_gem_fb_create_with_dirty,
        .atomic_check = drm_atomic_helper_check,
        .atomic_commit = drm_atomic_helper_commit,
 };
 
 static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
-                       const struct drm_framebuffer_funcs *fb_funcs,
                        struct drm_driver *driver)
 {
        struct drm_device *drm;
 
-       mutex_init(&tdev->dirty_lock);
-       tdev->fb_funcs = fb_funcs;
-
        /*
         * We don't embed drm_device, because that prevent us from using
         * devm_kzalloc() to allocate tinydrm_device in the driver since
@@ -86,7 +72,6 @@ static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
 static void tinydrm_fini(struct tinydrm_device *tdev)
 {
        drm_mode_config_cleanup(tdev->drm);
-       mutex_destroy(&tdev->dirty_lock);
        tdev->drm->dev_private = NULL;
        drm_dev_put(tdev->drm);
 }
@@ -100,7 +85,6 @@ static void devm_tinydrm_release(void *data)
  * devm_tinydrm_init - Initialize tinydrm device
  * @parent: Parent device object
  * @tdev: tinydrm device
- * @fb_funcs: Framebuffer functions
  * @driver: DRM driver
  *
  * This function initializes @tdev, the underlying DRM device and it's
@@ -111,12 +95,11 @@ static void devm_tinydrm_release(void *data)
  * Zero on success, negative error code on failure.
  */
 int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
-                     const struct drm_framebuffer_funcs *fb_funcs,
                      struct drm_driver *driver)
 {
        int ret;
 
-       ret = tinydrm_init(parent, tdev, fb_funcs, driver);
+       ret = tinydrm_init(parent, tdev, driver);
        if (ret)
                return ret;
 
index bf6bfbc5d412c8c2bb2e8a14fed1d16a139f1b88..2737b6fdadc85d5e60ef939fd5fd82d88ef5d526 100644 (file)
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_print.h>
-#include <drm/tinydrm/tinydrm.h>
+#include <drm/drm_rect.h>
 #include <drm/tinydrm/tinydrm-helpers.h>
-#include <uapi/drm/drm.h>
 
 static unsigned int spi_max;
 module_param(spi_max, uint, 0400);
 MODULE_PARM_DESC(spi_max, "Set a lower SPI max transfer size");
 
-/**
- * tinydrm_merge_clips - Merge clip rectangles
- * @dst: Destination clip rectangle
- * @src: Source clip rectangle(s)
- * @num_clips: Number of @src clip rectangles
- * @flags: Dirty fb ioctl flags
- * @max_width: Maximum width of @dst
- * @max_height: Maximum height of @dst
- *
- * This function merges @src clip rectangle(s) into @dst. If @src is NULL,
- * @max_width and @min_width is used to set a full @dst clip rectangle.
- *
- * Returns:
- * true if it's a full clip, false otherwise
- */
-bool tinydrm_merge_clips(struct drm_clip_rect *dst,
-                        struct drm_clip_rect *src, unsigned int num_clips,
-                        unsigned int flags, u32 max_width, u32 max_height)
-{
-       unsigned int i;
-
-       if (!src || !num_clips) {
-               dst->x1 = 0;
-               dst->x2 = max_width;
-               dst->y1 = 0;
-               dst->y2 = max_height;
-               return true;
-       }
-
-       dst->x1 = ~0;
-       dst->y1 = ~0;
-       dst->x2 = 0;
-       dst->y2 = 0;
-
-       for (i = 0; i < num_clips; i++) {
-               if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY)
-                       i++;
-               dst->x1 = min(dst->x1, src[i].x1);
-               dst->x2 = max(dst->x2, src[i].x2);
-               dst->y1 = min(dst->y1, src[i].y1);
-               dst->y2 = max(dst->y2, src[i].y2);
-       }
-
-       if (dst->x2 > max_width || dst->y2 > max_height ||
-           dst->x1 >= dst->x2 || dst->y1 >= dst->y2) {
-               DRM_DEBUG_KMS("Illegal clip: x1=%u, x2=%u, y1=%u, y2=%u\n",
-                             dst->x1, dst->x2, dst->y1, dst->y2);
-               dst->x1 = 0;
-               dst->y1 = 0;
-               dst->x2 = max_width;
-               dst->y2 = max_height;
-       }
-
-       return (dst->x2 - dst->x1) == max_width &&
-              (dst->y2 - dst->y1) == max_height;
-}
-EXPORT_SYMBOL(tinydrm_merge_clips);
-
-int tinydrm_fb_dirty(struct drm_framebuffer *fb,
-                    struct drm_file *file_priv,
-                    unsigned int flags, unsigned int color,
-                    struct drm_clip_rect *clips,
-                    unsigned int num_clips)
-{
-       struct tinydrm_device *tdev = fb->dev->dev_private;
-       struct drm_plane *plane = &tdev->pipe.plane;
-       int ret = 0;
-
-       drm_modeset_lock(&plane->mutex, NULL);
-
-       /* fbdev can flush even when we're not interested */
-       if (plane->state->fb == fb) {
-               mutex_lock(&tdev->dirty_lock);
-               ret = tdev->fb_dirty(fb, file_priv, flags,
-                                    color, clips, num_clips);
-               mutex_unlock(&tdev->dirty_lock);
-       }
-
-       drm_modeset_unlock(&plane->mutex);
-
-       if (ret)
-               dev_err_once(fb->dev->dev,
-                            "Failed to update display %d\n", ret);
-
-       return ret;
-}
-EXPORT_SYMBOL(tinydrm_fb_dirty);
-
 /**
  * tinydrm_memcpy - Copy clip buffer
  * @dst: Destination buffer
@@ -122,7 +34,7 @@ EXPORT_SYMBOL(tinydrm_fb_dirty);
  * @clip: Clip rectangle area to copy
  */
 void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
-                   struct drm_clip_rect *clip)
+                   struct drm_rect *clip)
 {
        unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0);
        unsigned int pitch = fb->pitches[0];
@@ -146,7 +58,7 @@ EXPORT_SYMBOL(tinydrm_memcpy);
  * @clip: Clip rectangle area to copy
  */
 void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb,
-                   struct drm_clip_rect *clip)
+                   struct drm_rect *clip)
 {
        size_t len = (clip->x2 - clip->x1) * sizeof(u16);
        unsigned int x, y;
@@ -186,7 +98,7 @@ EXPORT_SYMBOL(tinydrm_swab16);
  */
 void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr,
                                struct drm_framebuffer *fb,
-                               struct drm_clip_rect *clip, bool swap)
+                               struct drm_rect *clip, bool swap)
 {
        size_t len = (clip->x2 - clip->x1) * sizeof(u32);
        unsigned int x, y;
@@ -235,7 +147,7 @@ EXPORT_SYMBOL(tinydrm_xrgb8888_to_rgb565);
  * ITU BT.601 is used for the RGB -> luma (brightness) conversion.
  */
 void tinydrm_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
-                              struct drm_clip_rect *clip)
+                              struct drm_rect *clip)
 {
        unsigned int len = (clip->x2 - clip->x1) * sizeof(u32);
        unsigned int x, y;
index d4576d6e8ce4a8b3f29c7dae206a284c48291f62..323564329535ef6b6bcb955d96411231d4e3c78f 100644 (file)
@@ -13,7 +13,6 @@
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_modes.h>
 #include <drm/drm_print.h>
-#include <drm/drm_vblank.h>
 #include <drm/tinydrm/tinydrm.h>
 
 struct tinydrm_connector {
@@ -111,36 +110,6 @@ tinydrm_connector_create(struct drm_device *drm,
        return connector;
 }
 
-/**
- * tinydrm_display_pipe_update - Display pipe update helper
- * @pipe: Simple display pipe
- * @old_state: Old plane state
- *
- * This function does a full framebuffer flush if the plane framebuffer
- * has changed. It also handles vblank events. Drivers can use this as their
- * &drm_simple_display_pipe_funcs->update callback.
- */
-void tinydrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
-                                struct drm_plane_state *old_state)
-{
-       struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-       struct drm_framebuffer *fb = pipe->plane.state->fb;
-       struct drm_crtc *crtc = &tdev->pipe.crtc;
-
-       if (fb && (fb != old_state->fb)) {
-               if (tdev->fb_dirty)
-                       tdev->fb_dirty(fb, NULL, 0, 0, NULL, 0);
-       }
-
-       if (crtc->state->event) {
-               spin_lock_irq(&crtc->dev->event_lock);
-               drm_crtc_send_vblank_event(crtc, crtc->state->event);
-               spin_unlock_irq(&crtc->dev->event_lock);
-               crtc->state->event = NULL;
-       }
-}
-EXPORT_SYMBOL(tinydrm_display_pipe_update);
-
 static int tinydrm_rotate_mode(struct drm_display_mode *mode,
                               unsigned int rotation)
 {
index 3ae11aa4b73b50831fe272cbda23c816600f108d..8bbd0beafc6a415fb134db7ddacb03e62685c2f5 100644 (file)
@@ -176,7 +176,7 @@ out_enable:
 static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = {
        .enable = yx240qv29_enable,
        .disable = mipi_dbi_pipe_disable,
-       .update = tinydrm_display_pipe_update,
+       .update = mipi_dbi_pipe_update,
        .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
 };
 
index b0ad58b972272861439a449a03947e2cc50426cf..43a3b68d90a20a1f6e023433f16e840d2eb038e9 100644 (file)
 #include <linux/spi/spi.h>
 #include <video/mipi_display.h>
 
+#include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_rect.h>
+#include <drm/drm_vblank.h>
 #include <drm/tinydrm/mipi-dbi.h>
 #include <drm/tinydrm/tinydrm-helpers.h>
 
@@ -75,16 +78,14 @@ static inline int ili9225_command(struct mipi_dbi *mipi, u8 cmd, u16 data)
        return mipi_dbi_command_buf(mipi, cmd, par, 2);
 }
 
-static int ili9225_fb_dirty(struct drm_framebuffer *fb,
-                           struct drm_file *file_priv, unsigned int flags,
-                           unsigned int color, struct drm_clip_rect *clips,
-                           unsigned int num_clips)
+static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 {
        struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
        struct tinydrm_device *tdev = fb->dev->dev_private;
        struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       unsigned int height = rect->y2 - rect->y1;
+       unsigned int width = rect->x2 - rect->x1;
        bool swap = mipi->swap_bytes;
-       struct drm_clip_rect clip;
        u16 x_start, y_start;
        u16 x1, x2, y1, y2;
        int ret = 0;
@@ -92,54 +93,52 @@ static int ili9225_fb_dirty(struct drm_framebuffer *fb,
        void *tr;
 
        if (!mipi->enabled)
-               return 0;
+               return;
 
-       full = tinydrm_merge_clips(&clip, clips, num_clips, flags,
-                                  fb->width, fb->height);
+       full = width == fb->width && height == fb->height;
 
-       DRM_DEBUG("Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", fb->base.id,
-                 clip.x1, clip.x2, clip.y1, clip.y2);
+       DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
 
        if (!mipi->dc || !full || swap ||
            fb->format->format == DRM_FORMAT_XRGB8888) {
                tr = mipi->tx_buf;
-               ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, swap);
+               ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, rect, swap);
                if (ret)
-                       return ret;
+                       goto err_msg;
        } else {
                tr = cma_obj->vaddr;
        }
 
        switch (mipi->rotation) {
        default:
-               x1 = clip.x1;
-               x2 = clip.x2 - 1;
-               y1 = clip.y1;
-               y2 = clip.y2 - 1;
+               x1 = rect->x1;
+               x2 = rect->x2 - 1;
+               y1 = rect->y1;
+               y2 = rect->y2 - 1;
                x_start = x1;
                y_start = y1;
                break;
        case 90:
-               x1 = clip.y1;
-               x2 = clip.y2 - 1;
-               y1 = fb->width - clip.x2;
-               y2 = fb->width - clip.x1 - 1;
+               x1 = rect->y1;
+               x2 = rect->y2 - 1;
+               y1 = fb->width - rect->x2;
+               y2 = fb->width - rect->x1 - 1;
                x_start = x1;
                y_start = y2;
                break;
        case 180:
-               x1 = fb->width - clip.x2;
-               x2 = fb->width - clip.x1 - 1;
-               y1 = fb->height - clip.y2;
-               y2 = fb->height - clip.y1 - 1;
+               x1 = fb->width - rect->x2;
+               x2 = fb->width - rect->x1 - 1;
+               y1 = fb->height - rect->y2;
+               y2 = fb->height - rect->y1 - 1;
                x_start = x2;
                y_start = y2;
                break;
        case 270:
-               x1 = fb->height - clip.y2;
-               x2 = fb->height - clip.y1 - 1;
-               y1 = clip.x1;
-               y2 = clip.x2 - 1;
+               x1 = fb->height - rect->y2;
+               x2 = fb->height - rect->y1 - 1;
+               y1 = rect->x1;
+               y2 = rect->x2 - 1;
                x_start = x2;
                y_start = y1;
                break;
@@ -154,16 +153,29 @@ static int ili9225_fb_dirty(struct drm_framebuffer *fb,
        ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_2, y_start);
 
        ret = mipi_dbi_command_buf(mipi, ILI9225_WRITE_DATA_TO_GRAM, tr,
-                               (clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2);
-
-       return ret;
+                                  width * height * 2);
+err_msg:
+       if (ret)
+               dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
 }
 
-static const struct drm_framebuffer_funcs ili9225_fb_funcs = {
-       .destroy        = drm_gem_fb_destroy,
-       .create_handle  = drm_gem_fb_create_handle,
-       .dirty          = tinydrm_fb_dirty,
-};
+static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe,
+                               struct drm_plane_state *old_state)
+{
+       struct drm_plane_state *state = pipe->plane.state;
+       struct drm_crtc *crtc = &pipe->crtc;
+       struct drm_rect rect;
+
+       if (drm_atomic_helper_damage_merged(old_state, state, &rect))
+               ili9225_fb_dirty(state->fb, &rect);
+
+       if (crtc->state->event) {
+               spin_lock_irq(&crtc->dev->event_lock);
+               drm_crtc_send_vblank_event(crtc, crtc->state->event);
+               spin_unlock_irq(&crtc->dev->event_lock);
+               crtc->state->event = NULL;
+       }
+}
 
 static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
                                struct drm_crtc_state *crtc_state,
@@ -171,7 +183,14 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
 {
        struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
        struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct drm_framebuffer *fb = plane_state->fb;
        struct device *dev = tdev->drm->dev;
+       struct drm_rect rect = {
+               .x1 = 0,
+               .x2 = fb->width,
+               .y1 = 0,
+               .y2 = fb->height,
+       };
        int ret;
        u8 am_id;
 
@@ -259,7 +278,8 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
 
        ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x1017);
 
-       mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
+       mipi->enabled = true;
+       ili9225_fb_dirty(fb, &rect);
 }
 
 static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
@@ -304,59 +324,10 @@ static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 cmd, u8 *par,
        return tinydrm_spi_transfer(spi, speed_hz, NULL, bpw, par, num);
 }
 
-static const u32 ili9225_formats[] = {
-       DRM_FORMAT_RGB565,
-       DRM_FORMAT_XRGB8888,
-};
-
-static int ili9225_init(struct device *dev, struct mipi_dbi *mipi,
-                       const struct drm_simple_display_pipe_funcs *pipe_funcs,
-                       struct drm_driver *driver,
-                       const struct drm_display_mode *mode,
-                       unsigned int rotation)
-{
-       size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
-       struct tinydrm_device *tdev = &mipi->tinydrm;
-       int ret;
-
-       if (!mipi->command)
-               return -EINVAL;
-
-       mutex_init(&mipi->cmdlock);
-
-       mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL);
-       if (!mipi->tx_buf)
-               return -ENOMEM;
-
-       ret = devm_tinydrm_init(dev, tdev, &ili9225_fb_funcs, driver);
-       if (ret)
-               return ret;
-
-       tdev->fb_dirty = ili9225_fb_dirty;
-
-       ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
-                                       DRM_MODE_CONNECTOR_VIRTUAL,
-                                       ili9225_formats,
-                                       ARRAY_SIZE(ili9225_formats), mode,
-                                       rotation);
-       if (ret)
-               return ret;
-
-       tdev->drm->mode_config.preferred_depth = 16;
-       mipi->rotation = rotation;
-
-       drm_mode_config_reset(tdev->drm);
-
-       DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n",
-                     tdev->drm->mode_config.preferred_depth, rotation);
-
-       return 0;
-}
-
 static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
        .enable         = ili9225_pipe_enable,
        .disable        = ili9225_pipe_disable,
-       .update         = tinydrm_display_pipe_update,
+       .update         = ili9225_pipe_update,
        .prepare_fb     = drm_gem_fb_simple_display_pipe_prepare_fb,
 };
 
@@ -423,8 +394,8 @@ static int ili9225_probe(struct spi_device *spi)
        /* override the command function set in  mipi_dbi_spi_init() */
        mipi->command = ili9225_dbi_command;
 
-       ret = ili9225_init(&spi->dev, mipi, &ili9225_pipe_funcs,
-                          &ili9225_driver, &ili9225_mode, rotation);
+       ret = mipi_dbi_init(&spi->dev, mipi, &ili9225_pipe_funcs,
+                           &ili9225_driver, &ili9225_mode, rotation);
        if (ret)
                return ret;
 
index bcdf10906adea3e561c675cf3413cdf7be03ce59..713bb2dd7e04c6582ab516dbd41371b922b862b4 100644 (file)
@@ -132,7 +132,7 @@ out_enable:
 static const struct drm_simple_display_pipe_funcs ili9341_pipe_funcs = {
        .enable = yx240qv29_enable,
        .disable = mipi_dbi_pipe_disable,
-       .update = tinydrm_display_pipe_update,
+       .update = mipi_dbi_pipe_update,
        .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
 };
 
index 97805ca37a04f08792acaac8399aa9b31f2ea1d5..82a92ec9ae3cedbe776a1bd9997c8b186d110918 100644 (file)
@@ -140,7 +140,7 @@ out_enable:
 static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = {
        .enable = mi0283qt_enable,
        .disable = mipi_dbi_pipe_disable,
-       .update = tinydrm_display_pipe_update,
+       .update = mipi_dbi_pipe_update,
        .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
 };
 
index 10294e1283dd2c9a2fe3cafb48d8e82f1828e63a..918f77c7de34e149080808e0161bad941bd8b3e3 100644 (file)
 #include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 
+#include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_rect.h>
 #include <drm/tinydrm/mipi-dbi.h>
 #include <drm/tinydrm/tinydrm-helpers.h>
-#include <uapi/drm/drm.h>
 #include <video/mipi_display.h>
 
 #define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */
@@ -172,7 +174,7 @@ EXPORT_SYMBOL(mipi_dbi_command_buf);
  * Zero on success, negative error code on failure.
  */
 int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
-                     struct drm_clip_rect *clip, bool swap)
+                     struct drm_rect *clip, bool swap)
 {
        struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
        struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
@@ -211,58 +213,75 @@ int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
 }
 EXPORT_SYMBOL(mipi_dbi_buf_copy);
 
-static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb,
-                            struct drm_file *file_priv,
-                            unsigned int flags, unsigned int color,
-                            struct drm_clip_rect *clips,
-                            unsigned int num_clips)
+static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 {
        struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
        struct tinydrm_device *tdev = fb->dev->dev_private;
        struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       unsigned int height = rect->y2 - rect->y1;
+       unsigned int width = rect->x2 - rect->x1;
        bool swap = mipi->swap_bytes;
-       struct drm_clip_rect clip;
        int ret = 0;
        bool full;
        void *tr;
 
        if (!mipi->enabled)
-               return 0;
+               return;
 
-       full = tinydrm_merge_clips(&clip, clips, num_clips, flags,
-                                  fb->width, fb->height);
+       full = width == fb->width && height == fb->height;
 
-       DRM_DEBUG("Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", fb->base.id,
-                 clip.x1, clip.x2, clip.y1, clip.y2);
+       DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
 
        if (!mipi->dc || !full || swap ||
            fb->format->format == DRM_FORMAT_XRGB8888) {
                tr = mipi->tx_buf;
-               ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, swap);
+               ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, rect, swap);
                if (ret)
-                       return ret;
+                       goto err_msg;
        } else {
                tr = cma_obj->vaddr;
        }
 
        mipi_dbi_command(mipi, MIPI_DCS_SET_COLUMN_ADDRESS,
-                        (clip.x1 >> 8) & 0xFF, clip.x1 & 0xFF,
-                        ((clip.x2 - 1) >> 8) & 0xFF, (clip.x2 - 1) & 0xFF);
+                        (rect->x1 >> 8) & 0xff, rect->x1 & 0xff,
+                        ((rect->x2 - 1) >> 8) & 0xff, (rect->x2 - 1) & 0xff);
        mipi_dbi_command(mipi, MIPI_DCS_SET_PAGE_ADDRESS,
-                        (clip.y1 >> 8) & 0xFF, clip.y1 & 0xFF,
-                        ((clip.y2 - 1) >> 8) & 0xFF, (clip.y2 - 1) & 0xFF);
+                        (rect->y1 >> 8) & 0xff, rect->y1 & 0xff,
+                        ((rect->y2 - 1) >> 8) & 0xff, (rect->y2 - 1) & 0xff);
 
        ret = mipi_dbi_command_buf(mipi, MIPI_DCS_WRITE_MEMORY_START, tr,
-                               (clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2);
-
-       return ret;
+                                  width * height * 2);
+err_msg:
+       if (ret)
+               dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
 }
 
-static const struct drm_framebuffer_funcs mipi_dbi_fb_funcs = {
-       .destroy        = drm_gem_fb_destroy,
-       .create_handle  = drm_gem_fb_create_handle,
-       .dirty          = tinydrm_fb_dirty,
-};
+/**
+ * mipi_dbi_pipe_update - Display pipe update helper
+ * @pipe: Simple display pipe
+ * @old_state: Old plane state
+ *
+ * This function handles framebuffer flushing and vblank events. Drivers can use
+ * this as their &drm_simple_display_pipe_funcs->update callback.
+ */
+void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe,
+                         struct drm_plane_state *old_state)
+{
+       struct drm_plane_state *state = pipe->plane.state;
+       struct drm_crtc *crtc = &pipe->crtc;
+       struct drm_rect rect;
+
+       if (drm_atomic_helper_damage_merged(old_state, state, &rect))
+               mipi_dbi_fb_dirty(state->fb, &rect);
+
+       if (crtc->state->event) {
+               spin_lock_irq(&crtc->dev->event_lock);
+               drm_crtc_send_vblank_event(crtc, crtc->state->event);
+               spin_unlock_irq(&crtc->dev->event_lock);
+               crtc->state->event = NULL;
+       }
+}
+EXPORT_SYMBOL(mipi_dbi_pipe_update);
 
 /**
  * mipi_dbi_enable_flush - MIPI DBI enable helper
@@ -273,18 +292,25 @@ static const struct drm_framebuffer_funcs mipi_dbi_fb_funcs = {
  * This function sets &mipi_dbi->enabled, flushes the whole framebuffer and
  * enables the backlight. Drivers can use this in their
  * &drm_simple_display_pipe_funcs->enable callback.
+ *
+ * Note: Drivers which don't use mipi_dbi_pipe_update() because they have custom
+ * framebuffer flushing, can't use this function since they both use the same
+ * flushing code.
  */
 void mipi_dbi_enable_flush(struct mipi_dbi *mipi,
                           struct drm_crtc_state *crtc_state,
                           struct drm_plane_state *plane_state)
 {
-       struct tinydrm_device *tdev = &mipi->tinydrm;
        struct drm_framebuffer *fb = plane_state->fb;
+       struct drm_rect rect = {
+               .x1 = 0,
+               .x2 = fb->width,
+               .y1 = 0,
+               .y2 = fb->height,
+       };
 
        mipi->enabled = true;
-       if (fb)
-               tdev->fb_dirty(fb, NULL, 0, 0, NULL, 0);
-
+       mipi_dbi_fb_dirty(fb, &rect);
        backlight_enable(mipi->backlight);
 }
 EXPORT_SYMBOL(mipi_dbi_enable_flush);
@@ -376,12 +402,10 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
        if (!mipi->tx_buf)
                return -ENOMEM;
 
-       ret = devm_tinydrm_init(dev, tdev, &mipi_dbi_fb_funcs, driver);
+       ret = devm_tinydrm_init(dev, tdev, driver);
        if (ret)
                return ret;
 
-       tdev->fb_dirty = mipi_dbi_fb_dirty;
-
        /* TODO: Maybe add DRM_MODE_CONNECTOR_SPI */
        ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
                                        DRM_MODE_CONNECTOR_VIRTUAL,
@@ -391,6 +415,8 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
        if (ret)
                return ret;
 
+       drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
+
        tdev->drm->mode_config.preferred_depth = 16;
        mipi->rotation = rotation;
 
index b2a8f894946a4a56c2ab67e58e4751c46b447717..b037c6540cf389de2dd8777ab909309bcdf83a10 100644 (file)
 #include <linux/spi/spi.h>
 #include <linux/thermal.h>
 
+#include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_rect.h>
+#include <drm/drm_vblank.h>
 #include <drm/tinydrm/tinydrm.h>
 #include <drm/tinydrm/tinydrm-helpers.h>
 
@@ -522,17 +525,13 @@ static void repaper_gray8_to_mono_reversed(u8 *buf, u32 width, u32 height)
                }
 }
 
-static int repaper_fb_dirty(struct drm_framebuffer *fb,
-                           struct drm_file *file_priv,
-                           unsigned int flags, unsigned int color,
-                           struct drm_clip_rect *clips,
-                           unsigned int num_clips)
+static int repaper_fb_dirty(struct drm_framebuffer *fb)
 {
        struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
        struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
        struct tinydrm_device *tdev = fb->dev->dev_private;
        struct repaper_epd *epd = epd_from_tinydrm(tdev);
-       struct drm_clip_rect clip;
+       struct drm_rect clip;
        u8 *buf = NULL;
        int ret = 0;
 
@@ -625,12 +624,6 @@ out_free:
        return ret;
 }
 
-static const struct drm_framebuffer_funcs repaper_fb_funcs = {
-       .destroy        = drm_gem_fb_destroy,
-       .create_handle  = drm_gem_fb_create_handle,
-       .dirty          = tinydrm_fb_dirty,
-};
-
 static void power_off(struct repaper_epd *epd)
 {
        /* Turn off power and all signals */
@@ -794,9 +787,7 @@ static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
 
        DRM_DEBUG_DRIVER("\n");
 
-       mutex_lock(&tdev->dirty_lock);
        epd->enabled = false;
-       mutex_unlock(&tdev->dirty_lock);
 
        /* Nothing frame */
        for (line = 0; line < epd->height; line++)
@@ -839,10 +830,28 @@ static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
        power_off(epd);
 }
 
+static void repaper_pipe_update(struct drm_simple_display_pipe *pipe,
+                               struct drm_plane_state *old_state)
+{
+       struct drm_plane_state *state = pipe->plane.state;
+       struct drm_crtc *crtc = &pipe->crtc;
+       struct drm_rect rect;
+
+       if (drm_atomic_helper_damage_merged(old_state, state, &rect))
+               repaper_fb_dirty(state->fb);
+
+       if (crtc->state->event) {
+               spin_lock_irq(&crtc->dev->event_lock);
+               drm_crtc_send_vblank_event(crtc, crtc->state->event);
+               spin_unlock_irq(&crtc->dev->event_lock);
+               crtc->state->event = NULL;
+       }
+}
+
 static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = {
        .enable = repaper_pipe_enable,
        .disable = repaper_pipe_disable,
-       .update = tinydrm_display_pipe_update,
+       .update = repaper_pipe_update,
        .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
 };
 
@@ -1056,12 +1065,10 @@ static int repaper_probe(struct spi_device *spi)
 
        tdev = &epd->tinydrm;
 
-       ret = devm_tinydrm_init(dev, tdev, &repaper_fb_funcs, &repaper_driver);
+       ret = devm_tinydrm_init(dev, tdev, &repaper_driver);
        if (ret)
                return ret;
 
-       tdev->fb_dirty = repaper_fb_dirty;
-
        ret = tinydrm_display_pipe_init(tdev, &repaper_pipe_funcs,
                                        DRM_MODE_CONNECTOR_VIRTUAL,
                                        repaper_formats,
index bf518167760af4216207d03433788019a42195d9..01a8077954b341046087ae0d8c6230e10ff18707 100644 (file)
 #include <linux/spi/spi.h>
 #include <video/mipi_display.h>
 
+#include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_rect.h>
+#include <drm/drm_vblank.h>
 #include <drm/tinydrm/mipi-dbi.h>
 #include <drm/tinydrm/tinydrm-helpers.h>
 
@@ -62,7 +65,7 @@ static const u8 st7586_lookup[] = { 0x7, 0x4, 0x2, 0x0 };
 
 static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr,
                                       struct drm_framebuffer *fb,
-                                      struct drm_clip_rect *clip)
+                                      struct drm_rect *clip)
 {
        size_t len = (clip->x2 - clip->x1) * (clip->y2 - clip->y1);
        unsigned int x, y;
@@ -88,7 +91,7 @@ static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr,
 }
 
 static int st7586_buf_copy(void *dst, struct drm_framebuffer *fb,
-                          struct drm_clip_rect *clip)
+                          struct drm_rect *clip)
 {
        struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
        struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
@@ -111,57 +114,62 @@ static int st7586_buf_copy(void *dst, struct drm_framebuffer *fb,
        return ret;
 }
 
-static int st7586_fb_dirty(struct drm_framebuffer *fb,
-                          struct drm_file *file_priv, unsigned int flags,
-                          unsigned int color, struct drm_clip_rect *clips,
-                          unsigned int num_clips)
+static void st7586_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 {
        struct tinydrm_device *tdev = fb->dev->dev_private;
        struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
-       struct drm_clip_rect clip;
        int start, end;
        int ret = 0;
 
        if (!mipi->enabled)
-               return 0;
-
-       tinydrm_merge_clips(&clip, clips, num_clips, flags, fb->width,
-                           fb->height);
+               return;
 
        /* 3 pixels per byte, so grow clip to nearest multiple of 3 */
-       clip.x1 = rounddown(clip.x1, 3);
-       clip.x2 = roundup(clip.x2, 3);
+       rect->x1 = rounddown(rect->x1, 3);
+       rect->x2 = roundup(rect->x2, 3);
 
-       DRM_DEBUG("Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", fb->base.id,
-                 clip.x1, clip.x2, clip.y1, clip.y2);
+       DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
 
-       ret = st7586_buf_copy(mipi->tx_buf, fb, &clip);
+       ret = st7586_buf_copy(mipi->tx_buf, fb, rect);
        if (ret)
-               return ret;
+               goto err_msg;
 
        /* Pixels are packed 3 per byte */
-       start = clip.x1 / 3;
-       end = clip.x2 / 3;
+       start = rect->x1 / 3;
+       end = rect->x2 / 3;
 
        mipi_dbi_command(mipi, MIPI_DCS_SET_COLUMN_ADDRESS,
                         (start >> 8) & 0xFF, start & 0xFF,
                         (end >> 8) & 0xFF, (end - 1) & 0xFF);
        mipi_dbi_command(mipi, MIPI_DCS_SET_PAGE_ADDRESS,
-                        (clip.y1 >> 8) & 0xFF, clip.y1 & 0xFF,
-                        (clip.y2 >> 8) & 0xFF, (clip.y2 - 1) & 0xFF);
+                        (rect->y1 >> 8) & 0xFF, rect->y1 & 0xFF,
+                        (rect->y2 >> 8) & 0xFF, (rect->y2 - 1) & 0xFF);
 
        ret = mipi_dbi_command_buf(mipi, MIPI_DCS_WRITE_MEMORY_START,
                                   (u8 *)mipi->tx_buf,
-                                  (end - start) * (clip.y2 - clip.y1));
-
-       return ret;
+                                  (end - start) * (rect->y2 - rect->y1));
+err_msg:
+       if (ret)
+               dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
 }
 
-static const struct drm_framebuffer_funcs st7586_fb_funcs = {
-       .destroy        = drm_gem_fb_destroy,
-       .create_handle  = drm_gem_fb_create_handle,
-       .dirty          = tinydrm_fb_dirty,
-};
+static void st7586_pipe_update(struct drm_simple_display_pipe *pipe,
+                              struct drm_plane_state *old_state)
+{
+       struct drm_plane_state *state = pipe->plane.state;
+       struct drm_crtc *crtc = &pipe->crtc;
+       struct drm_rect rect;
+
+       if (drm_atomic_helper_damage_merged(old_state, state, &rect))
+               st7586_fb_dirty(state->fb, &rect);
+
+       if (crtc->state->event) {
+               spin_lock_irq(&crtc->dev->event_lock);
+               drm_crtc_send_vblank_event(crtc, crtc->state->event);
+               spin_unlock_irq(&crtc->dev->event_lock);
+               crtc->state->event = NULL;
+       }
+}
 
 static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
                               struct drm_crtc_state *crtc_state,
@@ -169,6 +177,13 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
 {
        struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
        struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+       struct drm_framebuffer *fb = plane_state->fb;
+       struct drm_rect rect = {
+               .x1 = 0,
+               .x2 = fb->width,
+               .y1 = 0,
+               .y2 = fb->height,
+       };
        int ret;
        u8 addr_mode;
 
@@ -225,9 +240,10 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
 
        msleep(100);
 
-       mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON);
+       mipi->enabled = true;
+       st7586_fb_dirty(fb, &rect);
 
-       mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
+       mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON);
 }
 
 static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe)
@@ -263,12 +279,10 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
        if (!mipi->tx_buf)
                return -ENOMEM;
 
-       ret = devm_tinydrm_init(dev, tdev, &st7586_fb_funcs, driver);
+       ret = devm_tinydrm_init(dev, tdev, driver);
        if (ret)
                return ret;
 
-       tdev->fb_dirty = st7586_fb_dirty;
-
        ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
                                        DRM_MODE_CONNECTOR_VIRTUAL,
                                        st7586_formats,
@@ -277,6 +291,8 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
        if (ret)
                return ret;
 
+       drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
+
        tdev->drm->mode_config.preferred_depth = 32;
        mipi->rotation = rotation;
 
@@ -291,7 +307,7 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
 static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = {
        .enable         = st7586_pipe_enable,
        .disable        = st7586_pipe_disable,
-       .update         = tinydrm_display_pipe_update,
+       .update         = st7586_pipe_update,
        .prepare_fb     = drm_gem_fb_simple_display_pipe_prepare_fb,
 };
 
index 9bc93d5a04011ff523fc02bd7e7a6fbb5b25d11b..3bab9a9569a67744b86e856368a6cfbb30401e77 100644 (file)
@@ -106,7 +106,7 @@ static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe,
 static const struct drm_simple_display_pipe_funcs jd_t18003_t01_pipe_funcs = {
        .enable         = jd_t18003_t01_pipe_enable,
        .disable        = mipi_dbi_pipe_disable,
-       .update         = tinydrm_display_pipe_update,
+       .update         = mipi_dbi_pipe_update,
        .prepare_fb     = drm_gem_fb_simple_display_pipe_prepare_fb,
 };
 
index 4487660b26b8d198ba25f64728f88e09adccaf97..40c34a5bf1498223760b99146dd99159697abb11 100644 (file)
@@ -78,6 +78,9 @@ drm_atomic_helper_damage_iter_init(struct drm_atomic_helper_damage_iter *iter,
 bool
 drm_atomic_helper_damage_iter_next(struct drm_atomic_helper_damage_iter *iter,
                                   struct drm_rect *rect);
+bool drm_atomic_helper_damage_merged(const struct drm_plane_state *old_state,
+                                    struct drm_plane_state *state,
+                                    struct drm_rect *rect);
 
 /**
  * drm_helper_get_plane_damage_clips - Returns damage clips in &drm_rect.
index 8dbbe1eece1bd28eb18e4ee82788b8ad840b670f..4becb09975a457124489b9d6933b5e4829adc6b0 100644 (file)
@@ -2,31 +2,9 @@
 #ifndef __DRM_FB_CMA_HELPER_H__
 #define __DRM_FB_CMA_HELPER_H__
 
-struct drm_fbdev_cma;
-struct drm_gem_cma_object;
-
-struct drm_fb_helper_surface_size;
-struct drm_framebuffer_funcs;
-struct drm_fb_helper_funcs;
 struct drm_framebuffer;
-struct drm_fb_helper;
-struct drm_device;
-struct drm_file;
-struct drm_mode_fb_cmd2;
-struct drm_plane;
 struct drm_plane_state;
 
-int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp,
-                         unsigned int max_conn_count);
-void drm_fb_cma_fbdev_fini(struct drm_device *dev);
-
-struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
-       unsigned int preferred_bpp, unsigned int max_conn_count);
-void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma);
-
-void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma);
-void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma);
-
 struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
        unsigned int plane);
 
index bcb389f04618a25a924637e0ccbb4d4f7af340c7..b3d9d88ab2902e360b6adff982be9e5a438e7ba9 100644 (file)
@@ -143,6 +143,123 @@ struct drm_format_name_buf {
        char str[32];
 };
 
+/**
+ * drm_format_info_is_yuv_packed - check that the format info matches a YUV
+ * format with data laid in a single plane
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a packed YUV format.
+ */
+static inline bool
+drm_format_info_is_yuv_packed(const struct drm_format_info *info)
+{
+       return info->is_yuv && info->num_planes == 1;
+}
+
+/**
+ * drm_format_info_is_yuv_semiplanar - check that the format info matches a YUV
+ * format with data laid in two planes (luminance and chrominance)
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a semiplanar YUV format.
+ */
+static inline bool
+drm_format_info_is_yuv_semiplanar(const struct drm_format_info *info)
+{
+       return info->is_yuv && info->num_planes == 2;
+}
+
+/**
+ * drm_format_info_is_yuv_planar - check that the format info matches a YUV
+ * format with data laid in three planes (one for each YUV component)
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a planar YUV format.
+ */
+static inline bool
+drm_format_info_is_yuv_planar(const struct drm_format_info *info)
+{
+       return info->is_yuv && info->num_planes == 3;
+}
+
+/**
+ * drm_format_info_is_yuv_sampling_410 - check that the format info matches a
+ * YUV format with 4:1:0 sub-sampling
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a YUV format with 4:1:0
+ * sub-sampling.
+ */
+static inline bool
+drm_format_info_is_yuv_sampling_410(const struct drm_format_info *info)
+{
+       return info->is_yuv && info->hsub == 4 && info->vsub == 4;
+}
+
+/**
+ * drm_format_info_is_yuv_sampling_411 - check that the format info matches a
+ * YUV format with 4:1:1 sub-sampling
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a YUV format with 4:1:1
+ * sub-sampling.
+ */
+static inline bool
+drm_format_info_is_yuv_sampling_411(const struct drm_format_info *info)
+{
+       return info->is_yuv && info->hsub == 4 && info->vsub == 1;
+}
+
+/**
+ * drm_format_info_is_yuv_sampling_420 - check that the format info matches a
+ * YUV format with 4:2:0 sub-sampling
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a YUV format with 4:2:0
+ * sub-sampling.
+ */
+static inline bool
+drm_format_info_is_yuv_sampling_420(const struct drm_format_info *info)
+{
+       return info->is_yuv && info->hsub == 2 && info->vsub == 2;
+}
+
+/**
+ * drm_format_info_is_yuv_sampling_422 - check that the format info matches a
+ * YUV format with 4:2:2 sub-sampling
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a YUV format with 4:2:2
+ * sub-sampling.
+ */
+static inline bool
+drm_format_info_is_yuv_sampling_422(const struct drm_format_info *info)
+{
+       return info->is_yuv && info->hsub == 2 && info->vsub == 1;
+}
+
+/**
+ * drm_format_info_is_yuv_sampling_444 - check that the format info matches a
+ * YUV format with 4:4:4 sub-sampling
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a YUV format with 4:4:4
+ * sub-sampling.
+ */
+static inline bool
+drm_format_info_is_yuv_sampling_444(const struct drm_format_info *info)
+{
+       return info->is_yuv && info->hsub == 1 && info->vsub == 1;
+}
+
 const struct drm_format_info *__drm_format_info(u32 format);
 const struct drm_format_info *drm_format_info(u32 format);
 const struct drm_format_info *
index a38de7eb55b49fde782b78214dfaa804052bca08..7f307e834eef3a66739cf3b3920dd14ffccfb66b 100644 (file)
@@ -25,6 +25,9 @@ drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
 struct drm_framebuffer *
 drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
                  const struct drm_mode_fb_cmd2 *mode_cmd);
+struct drm_framebuffer *
+drm_gem_fb_create_with_dirty(struct drm_device *dev, struct drm_file *file,
+                            const struct drm_mode_fb_cmd2 *mode_cmd);
 
 int drm_gem_fb_prepare_fb(struct drm_plane *plane,
                          struct drm_plane_state *state);
index 8163d35f8327af0abf6993f939aa4c0bd9a3cce3..07b8e9f04599801434041b7a23e5f914bbaa3a10 100644 (file)
@@ -71,7 +71,7 @@
  * FIXME: All users of drm_can_sleep should be removed (see todo.rst)
  *
  * Returns:
- * True if kgdb is active or we are in an atomic context or irqs are disabled
+ * False if kgdb is active, we are in atomic context or irqs are disabled.
  */
 static inline bool drm_can_sleep(void)
 {
index b8ba5886198678b9fd490a8794e8c794fe47bd47..f4ec2834bc229ba55939ea8a3a64aeb866536d2d 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <drm/tinydrm/tinydrm.h>
 
+struct drm_rect;
 struct spi_device;
 struct gpio_desc;
 struct regulator;
@@ -67,6 +68,8 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
                  const struct drm_simple_display_pipe_funcs *pipe_funcs,
                  struct drm_driver *driver,
                  const struct drm_display_mode *mode, unsigned int rotation);
+void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe,
+                         struct drm_plane_state *old_state);
 void mipi_dbi_enable_flush(struct mipi_dbi *mipi,
                           struct drm_crtc_state *crtc_state,
                           struct drm_plane_state *plan_state);
@@ -80,7 +83,7 @@ u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len);
 int mipi_dbi_command_read(struct mipi_dbi *mipi, u8 cmd, u8 *val);
 int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len);
 int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
-                     struct drm_clip_rect *clip, bool swap);
+                     struct drm_rect *clip, bool swap);
 /**
  * mipi_dbi_command - MIPI DCS command with optional parameter(s)
  * @mipi: MIPI structure
index 5b96f0b12c8cefb76554abf8faf281a5689634e5..f0d598789e4d34adbd461215c6782df6337735ae 100644 (file)
@@ -11,8 +11,8 @@
 #define __LINUX_TINYDRM_HELPERS_H
 
 struct backlight_device;
-struct tinydrm_device;
-struct drm_clip_rect;
+struct drm_framebuffer;
+struct drm_rect;
 struct spi_transfer;
 struct spi_message;
 struct spi_device;
@@ -33,23 +33,15 @@ static inline bool tinydrm_machine_little_endian(void)
 #endif
 }
 
-bool tinydrm_merge_clips(struct drm_clip_rect *dst,
-                        struct drm_clip_rect *src, unsigned int num_clips,
-                        unsigned int flags, u32 max_width, u32 max_height);
-int tinydrm_fb_dirty(struct drm_framebuffer *fb,
-                    struct drm_file *file_priv,
-                    unsigned int flags, unsigned int color,
-                    struct drm_clip_rect *clips,
-                    unsigned int num_clips);
 void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
-                   struct drm_clip_rect *clip);
+                   struct drm_rect *clip);
 void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb,
-                   struct drm_clip_rect *clip);
+                   struct drm_rect *clip);
 void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr,
                                struct drm_framebuffer *fb,
-                               struct drm_clip_rect *clip, bool swap);
+                               struct drm_rect *clip, bool swap);
 void tinydrm_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
-                              struct drm_clip_rect *clip);
+                              struct drm_rect *clip);
 
 size_t tinydrm_spi_max_transfer_size(struct spi_device *spi, size_t max_len);
 bool tinydrm_spi_bpw_supported(struct spi_device *spi, u8 bpw);
index 448aa5ea4722da2007fcfe8ee79855ccb22bb6ef..5621688edcc0aec7f9f23fd4ab60886a45502c8b 100644 (file)
 #ifndef __LINUX_TINYDRM_H
 #define __LINUX_TINYDRM_H
 
-#include <linux/mutex.h>
 #include <drm/drm_simple_kms_helper.h>
 
-struct drm_clip_rect;
 struct drm_driver;
-struct drm_file;
-struct drm_framebuffer;
-struct drm_framebuffer_funcs;
 
 /**
  * struct tinydrm_device - tinydrm device
@@ -32,24 +27,6 @@ struct tinydrm_device {
         * @pipe: Display pipe structure
         */
        struct drm_simple_display_pipe pipe;
-
-       /**
-        * @dirty_lock: Serializes framebuffer flushing
-        */
-       struct mutex dirty_lock;
-
-       /**
-        * @fb_funcs: Framebuffer functions used when creating framebuffers
-        */
-       const struct drm_framebuffer_funcs *fb_funcs;
-
-       /**
-        * @fb_dirty: Framebuffer dirty callback
-        */
-       int (*fb_dirty)(struct drm_framebuffer *framebuffer,
-                       struct drm_file *file_priv, unsigned flags,
-                       unsigned color, struct drm_clip_rect *clips,
-                       unsigned num_clips);
 };
 
 static inline struct tinydrm_device *
@@ -82,13 +59,10 @@ pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
        .clock = 1 /* pass validation */
 
 int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
-                     const struct drm_framebuffer_funcs *fb_funcs,
                      struct drm_driver *driver);
 int devm_tinydrm_register(struct tinydrm_device *tdev);
 void tinydrm_shutdown(struct tinydrm_device *tdev);
 
-void tinydrm_display_pipe_update(struct drm_simple_display_pipe *pipe,
-                                struct drm_plane_state *old_state);
 int
 tinydrm_display_pipe_init(struct tinydrm_device *tdev,
                          const struct drm_simple_display_pipe_funcs *funcs,
index bc8940ca280dc74c36c706dc6d7c831a39381cc4..c0ff417b477063d50eb7e168679843001a122efe 100644 (file)
@@ -40,6 +40,7 @@ struct dma_fence_array_cb {
  * @num_fences: number of fences in the array
  * @num_pending: fences in the array still pending
  * @fences: array of the fences
+ * @work: internal irq_work function
  */
 struct dma_fence_array {
        struct dma_fence base;
index 41106c8357477f001e47cf8d5b98da913a8547cc..91d08a23f125b078dda6abf29a44c86cc80d2914 100644 (file)
@@ -238,6 +238,8 @@ extern "C" {
 #define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06
 #define DRM_FORMAT_MOD_VENDOR_BROADCOM 0x07
 #define DRM_FORMAT_MOD_VENDOR_ARM     0x08
+#define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
+
 /* add more to the end as needed */
 
 #define DRM_FORMAT_RESERVED          ((1ULL << 56) - 1)
@@ -667,6 +669,20 @@ extern "C" {
  */
 #define AFBC_FORMAT_MOD_BCH     (1ULL << 11)
 
+/*
+ * Allwinner tiled modifier
+ *
+ * This tiling mode is implemented by the VPU found on all Allwinner platforms,
+ * codenamed sunxi. It is associated with a YUV format that uses either 2 or 3
+ * planes.
+ *
+ * With this tiling, the luminance samples are disposed in tiles representing
+ * 32x32 pixels and the chrominance samples in tiles representing 32x64 pixels.
+ * The pixel order in each tile is linear and the tiles are disposed linearly,
+ * both in row-major order.
+ */
+#define DRM_FORMAT_MOD_ALLWINNER_TILED fourcc_mod_code(ALLWINNER, 1)
+
 #if defined(__cplusplus)
 }
 #endif