drm/rockchip: gem: add get phys ioctl
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / rockchip / rockchip_drm_drv.c
index c65d88919d735b2c5a283d983f2e73bb241ac83b..67c12afef2bc09a41a82bb8e30f4ad2093b9e5e1 100644 (file)
@@ -27,6 +27,8 @@
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/of_graph.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/component.h>
 #include <linux/fence.h>
 #include <linux/console.h>
@@ -38,7 +40,6 @@
 #include "rockchip_drm_fb.h"
 #include "rockchip_drm_fbdev.h"
 #include "rockchip_drm_gem.h"
-#include "rockchip_drm_rga.h"
 
 #define DRIVER_NAME    "rockchip"
 #define DRIVER_DESC    "RockChip Soc DRM"
@@ -104,6 +105,74 @@ static struct drm_connector *find_connector_by_node(struct drm_device *drm_dev,
        return NULL;
 }
 
+static
+struct drm_connector *find_connector_by_bridge(struct drm_device *drm_dev,
+                                              struct device_node *node)
+{
+       struct device_node *np_encoder, *np_connector;
+       struct drm_encoder *encoder;
+       struct drm_connector *connector = NULL;
+       struct device_node *port, *endpoint;
+       bool encoder_bridge = false;
+       bool found_connector = false;
+
+       np_encoder = of_graph_get_remote_port_parent(node);
+       if (!np_encoder || !of_device_is_available(np_encoder))
+               goto err_put_encoder;
+       drm_for_each_encoder(encoder, drm_dev) {
+               if (encoder->port == np_encoder && encoder->bridge) {
+                       encoder_bridge = true;
+                       break;
+               }
+       }
+       if (!encoder_bridge) {
+               dev_err(drm_dev->dev, "can't found encoder bridge!\n");
+               goto err_put_encoder;
+       }
+       port = of_graph_get_port_by_id(np_encoder, 1);
+       if (!port) {
+               dev_err(drm_dev->dev, "can't found port point!\n");
+               goto err_put_encoder;
+       }
+       for_each_child_of_node(port, endpoint) {
+               np_connector = of_graph_get_remote_port_parent(endpoint);
+               if (!np_connector) {
+                       dev_err(drm_dev->dev,
+                               "can't found connector node, please init!\n");
+                       goto err_put_port;
+               }
+               if (!of_device_is_available(np_connector)) {
+                       of_node_put(np_connector);
+                       np_connector = NULL;
+                       continue;
+               } else {
+                       break;
+               }
+       }
+       if (!np_connector) {
+               dev_err(drm_dev->dev, "can't found available connector node!\n");
+               goto err_put_port;
+       }
+
+       drm_for_each_connector(connector, drm_dev) {
+               if (connector->port == np_connector) {
+                       found_connector = true;
+                       break;
+               }
+       }
+
+       if (!found_connector)
+               connector = NULL;
+
+       of_node_put(np_connector);
+err_put_port:
+       of_node_put(port);
+err_put_encoder:
+       of_node_put(np_encoder);
+
+       return connector;
+}
+
 void rockchip_free_loader_memory(struct drm_device *drm)
 {
        struct rockchip_drm_private *private = drm->dev_private;
@@ -255,7 +324,7 @@ get_framebuffer_by_node(struct drm_device *drm_dev, struct device_node *node)
        }
        bpp = val;
 
-       mode_cmd.pitches[0] = mode_cmd.width * bpp / 8;
+       mode_cmd.pitches[0] = ALIGN(mode_cmd.width * bpp, 32) / 8;
 
        switch (bpp) {
        case 16:
@@ -265,7 +334,7 @@ get_framebuffer_by_node(struct drm_device *drm_dev, struct device_node *node)
                mode_cmd.pixel_format = DRM_FORMAT_BGR888;
                break;
        case 32:
-               mode_cmd.pixel_format = DRM_FORMAT_XBGR8888;
+               mode_cmd.pixel_format = DRM_FORMAT_XRGB8888;
                break;
        default:
                pr_err("%s: unsupport to logo bpp %d\n", __func__, bpp);
@@ -296,6 +365,8 @@ of_parse_display_resource(struct drm_device *drm_dev, struct device_node *route)
 
        crtc = find_crtc_by_node(drm_dev, connect);
        connector = find_connector_by_node(drm_dev, connect);
+       if (!connector)
+               connector = find_connector_by_bridge(drm_dev, connect);
        if (!crtc || !connector) {
                dev_warn(drm_dev->dev,
                         "No available crtc or connector for display");
@@ -342,6 +413,7 @@ int setup_initial_state(struct drm_device *drm_dev,
        struct drm_plane_state *primary_state;
        struct drm_display_mode *mode = NULL;
        const struct drm_connector_helper_funcs *funcs;
+       const struct drm_encoder_helper_funcs *encoder_funcs;
        bool is_crtc_enabled = true;
        int hdisplay, vdisplay;
        int fb_width, fb_height;
@@ -361,6 +433,10 @@ int setup_initial_state(struct drm_device *drm_dev,
        if (funcs->loader_protect)
                funcs->loader_protect(connector, true);
        connector->loader_protect = true;
+       encoder_funcs = conn_state->best_encoder->helper_private;
+       if (encoder_funcs->loader_protect)
+               encoder_funcs->loader_protect(conn_state->best_encoder, true);
+       conn_state->best_encoder->loader_protect = true;
        num_modes = connector->funcs->fill_modes(connector, 4096, 4096);
        if (!num_modes) {
                dev_err(drm_dev->dev, "connector[%s] can't found any modes\n",
@@ -470,6 +546,9 @@ error:
        if (funcs->loader_protect)
                funcs->loader_protect(connector, false);
        connector->loader_protect = false;
+       if (encoder_funcs->loader_protect)
+               encoder_funcs->loader_protect(conn_state->best_encoder, false);
+       conn_state->best_encoder->loader_protect = false;
        return ret;
 }
 
@@ -478,7 +557,7 @@ static int update_state(struct drm_device *drm_dev,
                        struct rockchip_drm_mode_set *set,
                        unsigned int *plane_mask)
 {
-       struct drm_mode_config *mode_config = &drm_dev->mode_config;
+       struct rockchip_drm_private *priv = drm_dev->dev_private;
        struct drm_crtc *crtc = set->crtc;
        struct drm_connector *connector = set->connector;
        struct drm_display_mode *mode = set->mode;
@@ -507,7 +586,6 @@ static int update_state(struct drm_device *drm_dev,
        } else {
                const struct drm_encoder_helper_funcs *encoder_helper_funcs;
                const struct drm_connector_helper_funcs *connector_helper_funcs;
-               struct rockchip_drm_private *priv = drm_dev->dev_private;
                struct drm_encoder *encoder;
                int pipe = drm_crtc_index(crtc);
 
@@ -549,15 +627,15 @@ static int update_state(struct drm_device *drm_dev,
                 * some vop maybe not support ymirror, but force use it now.
                 */
                drm_atomic_plane_set_property(crtc->primary, primary_state,
-                                             mode_config->rotation_property,
-                                             BIT(DRM_REFLECT_Y));
+                                             priv->logo_ymirror_prop,
+                                             true);
 
        return ret;
 }
 
 static void show_loader_logo(struct drm_device *drm_dev)
 {
-       struct drm_atomic_state *state;
+       struct drm_atomic_state *state, *old_state;
        struct device_node *np = drm_dev->dev->of_node;
        struct drm_mode_config *mode_config = &drm_dev->mode_config;
        struct device_node *root, *route;
@@ -611,6 +689,14 @@ static void show_loader_logo(struct drm_device *drm_dev)
                goto err_free_state;
        }
 
+       old_state = drm_atomic_helper_duplicate_state(drm_dev,
+                                                     mode_config->acquire_ctx);
+       if (IS_ERR(old_state)) {
+               dev_err(drm_dev->dev, "failed to duplicate atomic state\n");
+               ret = PTR_ERR_OR_ZERO(old_state);
+               goto err_free_state;
+       }
+
        /*
         * The state save initial devices status, swap the state into
         * drm deivces as old state, so if new state come, can compare
@@ -623,7 +709,7 @@ static void show_loader_logo(struct drm_device *drm_dev)
        if (IS_ERR(state)) {
                dev_err(drm_dev->dev, "failed to duplicate atomic state\n");
                ret = PTR_ERR_OR_ZERO(state);
-               goto err_unlock;
+               goto err_free_old_state;
        }
        state->acquire_ctx = mode_config->acquire_ctx;
        list_for_each_entry(set, &mode_set_list, head)
@@ -636,24 +722,8 @@ static void show_loader_logo(struct drm_device *drm_dev)
        drm_atomic_clean_old_fb(drm_dev, plane_mask, ret);
 
        list_for_each_entry_safe(set, tmp, &mode_set_list, head) {
-               struct drm_crtc *crtc = set->crtc;
-
                list_del(&set->head);
                kfree(set);
-
-               /* FIXME:
-                * primary plane state rotation is not BIT(0), but we only want
-                * it effect on logo display, userspace may not known to clean
-                * this property, would get unexpect display, so force set
-                * primary rotation to BIT(0).
-                */
-               if (!crtc->primary || !crtc->primary->state)
-                       continue;
-
-               drm_atomic_plane_set_property(crtc->primary,
-                                             crtc->primary->state,
-                                             mode_config->rotation_property,
-                                             BIT(0));
        }
 
        /*
@@ -661,14 +731,22 @@ static void show_loader_logo(struct drm_device *drm_dev)
         */
        WARN_ON(ret == -EDEADLK);
 
-       if (ret)
-               goto err_free_state;
+       if (ret) {
+               /*
+                * restore display status if atomic commit failed.
+                */
+               drm_atomic_helper_swap_state(drm_dev, old_state);
+               goto err_free_old_state;
+       }
 
        rockchip_free_loader_memory(drm_dev);
+       drm_atomic_state_free(old_state);
 
        drm_modeset_unlock_all(drm_dev);
        return;
 
+err_free_old_state:
+       drm_atomic_state_free(old_state);
 err_free_state:
        drm_atomic_state_free(state);
 err_unlock:
@@ -676,6 +754,55 @@ err_unlock:
        if (ret)
                dev_err(drm_dev->dev, "failed to show loader logo\n");
 }
+
+static const char *const loader_protect_clocks[] __initconst = {
+       "hclk_vio",
+       "aclk_vio",
+       "aclk_vio0",
+};
+
+static struct clk **loader_clocks __initdata;
+static int __init rockchip_clocks_loader_protect(void)
+{
+       int nclocks = ARRAY_SIZE(loader_protect_clocks);
+       struct clk *clk;
+       int i;
+
+       loader_clocks = kcalloc(nclocks, sizeof(void *), GFP_KERNEL);
+       if (!loader_clocks)
+               return -ENOMEM;
+
+       for (i = 0; i < nclocks; i++) {
+               clk = __clk_lookup(loader_protect_clocks[i]);
+
+               if (clk) {
+                       loader_clocks[i] = clk;
+                       clk_prepare_enable(clk);
+               }
+       }
+
+       return 0;
+}
+fs_initcall(rockchip_clocks_loader_protect);
+
+static int __init rockchip_clocks_loader_unprotect(void)
+{
+       int i;
+
+       if (!loader_clocks)
+               return -ENODEV;
+
+       for (i = 0; i < ARRAY_SIZE(loader_protect_clocks); i++) {
+               struct clk *clk = loader_clocks[i];
+
+               if (clk)
+                       clk_disable_unprepare(clk);
+       }
+       kfree(loader_clocks);
+
+       return 0;
+}
+late_initcall_sync(rockchip_clocks_loader_unprotect);
 #endif
 
 /*
@@ -775,6 +902,29 @@ static void rockchip_drm_crtc_disable_vblank(struct drm_device *dev,
                priv->crtc_funcs[pipe]->disable_vblank(crtc);
 }
 
+static int rockchip_drm_fault_handler(struct iommu_domain *iommu,
+                                     struct device *dev,
+                                     unsigned long iova, int flags, void *arg)
+{
+       struct drm_device *drm_dev = arg;
+       struct rockchip_drm_private *priv = drm_dev->dev_private;
+       struct drm_crtc *crtc;
+
+       drm_for_each_crtc(crtc, drm_dev) {
+               int pipe = drm_crtc_index(crtc);
+
+               if (priv->crtc_funcs[pipe] &&
+                   priv->crtc_funcs[pipe]->regs_dump)
+                       priv->crtc_funcs[pipe]->regs_dump(crtc, NULL);
+
+               if (priv->crtc_funcs[pipe] &&
+                   priv->crtc_funcs[pipe]->debugfs_dump)
+                       priv->crtc_funcs[pipe]->debugfs_dump(crtc, NULL);
+       }
+
+       return 0;
+}
+
 static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
 {
        struct rockchip_drm_private *private = drm_dev->dev_private;
@@ -797,6 +947,9 @@ static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
        drm_mm_init(&private->mm, start, end - start + 1);
        mutex_init(&private->mm_lock);
 
+       iommu_set_fault_handler(private->domain, rockchip_drm_fault_handler,
+                               drm_dev);
+
        return 0;
 }
 
@@ -820,6 +973,9 @@ static int rockchip_drm_mm_dump(struct seq_file *s, void *data)
        struct rockchip_drm_private *priv = drm_dev->dev_private;
        int ret;
 
+       if (!priv->domain)
+               return 0;
+
        mutex_lock(&priv->mm_lock);
 
        ret = drm_mm_dump_table(s, &priv->mm);
@@ -877,6 +1033,54 @@ static void rockchip_drm_debugfs_cleanup(struct drm_minor *minor)
 }
 #endif
 
+static int rockchip_drm_create_properties(struct drm_device *dev)
+{
+       struct drm_property *prop;
+       struct rockchip_drm_private *private = dev->dev_private;
+       const struct drm_prop_enum_list cabc_mode_enum_list[] = {
+               { ROCKCHIP_DRM_CABC_MODE_DISABLE, "Disable" },
+               { ROCKCHIP_DRM_CABC_MODE_NORMAL, "Normal" },
+               { ROCKCHIP_DRM_CABC_MODE_LOWPOWER, "LowPower" },
+               { ROCKCHIP_DRM_CABC_MODE_USERSPACE, "Userspace" },
+       };
+
+       prop = drm_property_create_enum(dev, 0, "CABC_MODE", cabc_mode_enum_list,
+                       ARRAY_SIZE(cabc_mode_enum_list));
+
+       private->cabc_mode_property = prop;
+
+       prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CABC_LUT", 0);
+       if (!prop)
+               return -ENOMEM;
+       private->cabc_lut_property = prop;
+
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                                       "CABC_STAGE_UP", 0, 512);
+       if (!prop)
+               return -ENOMEM;
+       private->cabc_stage_up_property = prop;
+
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                                       "CABC_STAGE_DOWN", 0, 255);
+       if (!prop)
+               return -ENOMEM;
+       private->cabc_stage_down_property = prop;
+
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                                       "CABC_GLOBAL_DN", 0, 255);
+       if (!prop)
+               return -ENOMEM;
+       private->cabc_global_dn_property = prop;
+
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                                       "CABC_CALC_PIXEL_NUM", 0, 1000);
+       if (!prop)
+               return -ENOMEM;
+       private->cabc_calc_pixel_num_property = prop;
+
+       return 0;
+}
+
 static int rockchip_drm_bind(struct device *dev)
 {
        struct drm_device *drm_dev;
@@ -904,6 +1108,30 @@ static int rockchip_drm_bind(struct device *dev)
 
        drm_dev->dev_private = private;
 
+       private->hdmi_pll.pll = devm_clk_get(dev, "hdmi-tmds-pll");
+       if (PTR_ERR(private->hdmi_pll.pll) == -ENOENT) {
+               private->hdmi_pll.pll = NULL;
+       } else if (PTR_ERR(private->hdmi_pll.pll) == -EPROBE_DEFER) {
+               ret = -EPROBE_DEFER;
+               goto err_free;
+       } else if (IS_ERR(private->hdmi_pll.pll)) {
+               dev_err(dev, "failed to get hdmi-tmds-pll\n");
+               ret = PTR_ERR(private->hdmi_pll.pll);
+               goto err_free;
+       }
+
+       private->default_pll.pll = devm_clk_get(dev, "default-vop-pll");
+       if (PTR_ERR(private->default_pll.pll) == -ENOENT) {
+               private->default_pll.pll = NULL;
+       } else if (PTR_ERR(private->default_pll.pll) == -EPROBE_DEFER) {
+               ret = -EPROBE_DEFER;
+               goto err_free;
+       } else if (IS_ERR(private->default_pll.pll)) {
+               dev_err(dev, "failed to get default vop pll\n");
+               ret = PTR_ERR(private->default_pll.pll);
+               goto err_free;
+       }
+
 #ifdef CONFIG_DRM_DMA_SYNC
        private->cpu_fence_context = fence_context_alloc(1);
        atomic_set(&private->cpu_fence_seqno, 0);
@@ -912,6 +1140,7 @@ static int rockchip_drm_bind(struct device *dev)
        drm_mode_config_init(drm_dev);
 
        rockchip_drm_mode_config_init(drm_dev);
+       rockchip_drm_create_properties(drm_dev);
 
        ret = rockchip_drm_init_iommu(drm_dev);
        if (ret)
@@ -1121,13 +1350,8 @@ static const struct drm_ioctl_desc rockchip_ioctls[] = {
        DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CPU_RELEASE,
                          rockchip_gem_cpu_release_ioctl,
                          DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
-       DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_GET_VER, rockchip_rga_get_ver_ioctl,
-                         DRM_AUTH | DRM_RENDER_ALLOW),
-       DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_SET_CMDLIST,
-                         rockchip_rga_set_cmdlist_ioctl,
-                         DRM_AUTH | DRM_RENDER_ALLOW),
-       DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_EXEC, rockchip_rga_exec_ioctl,
-                         DRM_AUTH | DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET_PHYS, rockchip_gem_get_phys_ioctl,
+                         DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
 };
 
 static const struct file_operations rockchip_drm_driver_fops = {
@@ -1173,6 +1397,8 @@ static struct drm_driver rockchip_drm_driver = {
        .gem_prime_vmap         = rockchip_gem_prime_vmap,
        .gem_prime_vunmap       = rockchip_gem_prime_vunmap,
        .gem_prime_mmap         = rockchip_gem_mmap_buf,
+       .gem_prime_begin_cpu_access = rockchip_gem_prime_begin_cpu_access,
+       .gem_prime_end_cpu_access = rockchip_gem_prime_end_cpu_access,
 #ifdef CONFIG_DEBUG_FS
        .debugfs_init           = rockchip_drm_debugfs_init,
        .debugfs_cleanup        = rockchip_drm_debugfs_cleanup,
@@ -1346,6 +1572,12 @@ static int rockchip_drm_platform_probe(struct platform_device *pdev)
                of_node_put(port);
        }
 
+       port = of_parse_phandle(np, "backlight", 0);
+       if (port && of_device_is_available(port)) {
+               component_match_add(dev, &match, compare_of, port);
+               of_node_put(port);
+       }
+
        return component_master_add_with_match(dev, &rockchip_drm_ops, match);
 }