Merge drm/drm-next into drm-misc-next
[linux-block.git] / drivers / gpu / drm / tegra / dc.c
index f9845a50f86632c3a161444b7ad939d7b860f8aa..074563ca586c319a3cca056af3cb4561a2fd481f 100644 (file)
@@ -832,10 +832,14 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
        return &plane->base;
 }
 
-static const u32 tegra_cursor_plane_formats[] = {
+static const u32 tegra_legacy_cursor_plane_formats[] = {
        DRM_FORMAT_RGBA8888,
 };
 
+static const u32 tegra_cursor_plane_formats[] = {
+       DRM_FORMAT_ARGB8888,
+};
+
 static int tegra_cursor_atomic_check(struct drm_plane *plane,
                                     struct drm_atomic_state *state)
 {
@@ -875,12 +879,24 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
                                                                           plane);
        struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
        struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
-       u32 value = CURSOR_CLIP_DISPLAY;
+       struct tegra_drm *tegra = plane->dev->dev_private;
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+       u64 dma_mask = *dc->dev->dma_mask;
+#endif
+       unsigned int x, y;
+       u32 value = 0;
 
        /* rien ne va plus */
        if (!new_state->crtc || !new_state->fb)
                return;
 
+       /*
+        * Legacy display supports hardware clipping of the cursor, but
+        * nvdisplay relies on software to clip the cursor to the screen.
+        */
+       if (!dc->soc->has_nvdisplay)
+               value |= CURSOR_CLIP_DISPLAY;
+
        switch (new_state->crtc_w) {
        case 32:
                value |= CURSOR_SIZE_32x32;
@@ -908,7 +924,7 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
        tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);
 
 #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
-       value = (tegra_plane_state->iova[0] >> 32) & 0x3;
+       value = (tegra_plane_state->iova[0] >> 32) & (dma_mask >> 32);
        tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
 #endif
 
@@ -920,15 +936,39 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
        value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
        value &= ~CURSOR_DST_BLEND_MASK;
        value &= ~CURSOR_SRC_BLEND_MASK;
-       value |= CURSOR_MODE_NORMAL;
+
+       if (dc->soc->has_nvdisplay)
+               value &= ~CURSOR_COMPOSITION_MODE_XOR;
+       else
+               value |= CURSOR_MODE_NORMAL;
+
        value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
        value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
        value |= CURSOR_ALPHA;
        tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
 
+       /* nvdisplay relies on software for clipping */
+       if (dc->soc->has_nvdisplay) {
+               struct drm_rect src;
+
+               x = new_state->dst.x1;
+               y = new_state->dst.y1;
+
+               drm_rect_fp_to_int(&src, &new_state->src);
+
+               value = (src.y1 & tegra->vmask) << 16 | (src.x1 & tegra->hmask);
+               tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR);
+
+               value = (drm_rect_height(&src) & tegra->vmask) << 16 |
+                       (drm_rect_width(&src) & tegra->hmask);
+               tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR);
+       } else {
+               x = new_state->crtc_x;
+               y = new_state->crtc_y;
+       }
+
        /* position the cursor */
-       value = (new_state->crtc_y & 0x3fff) << 16 |
-               (new_state->crtc_x & 0x3fff);
+       value = ((y & tegra->vmask) << 16) | (x & tegra->hmask);
        tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
 }
 
@@ -987,8 +1027,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
        plane->index = 6;
        plane->dc = dc;
 
-       num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
-       formats = tegra_cursor_plane_formats;
+       if (!dc->soc->has_nvdisplay) {
+               num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats);
+               formats = tegra_legacy_cursor_plane_formats;
+       } else {
+               num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
+               formats = tegra_cursor_plane_formats;
+       }
 
        err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
                                       &tegra_plane_funcs, formats,
@@ -2041,6 +2086,16 @@ static bool tegra_dc_has_window_groups(struct tegra_dc *dc)
        return false;
 }
 
+static int tegra_dc_early_init(struct host1x_client *client)
+{
+       struct drm_device *drm = dev_get_drvdata(client->host);
+       struct tegra_drm *tegra = drm->dev_private;
+
+       tegra->num_crtcs++;
+
+       return 0;
+}
+
 static int tegra_dc_init(struct host1x_client *client)
 {
        struct drm_device *drm = dev_get_drvdata(client->host);
@@ -2051,6 +2106,12 @@ static int tegra_dc_init(struct host1x_client *client)
        struct drm_plane *cursor = NULL;
        int err;
 
+       /*
+        * DC has been reset by now, so VBLANK syncpoint can be released
+        * for general use.
+        */
+       host1x_syncpt_release_vblank_reservation(client, 26 + dc->pipe);
+
        /*
         * XXX do not register DCs with no window groups because we cannot
         * assign a primary plane to them, which in turn will cause KMS to
@@ -2117,6 +2178,12 @@ static int tegra_dc_init(struct host1x_client *client)
        if (dc->soc->pitch_align > tegra->pitch_align)
                tegra->pitch_align = dc->soc->pitch_align;
 
+       /* track maximum resolution */
+       if (dc->soc->has_nvdisplay)
+               drm->mode_config.max_width = drm->mode_config.max_height = 16384;
+       else
+               drm->mode_config.max_width = drm->mode_config.max_height = 4096;
+
        err = tegra_dc_rgb_init(drm, dc);
        if (err < 0 && err != -ENODEV) {
                dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
@@ -2147,7 +2214,7 @@ cleanup:
                drm_plane_cleanup(primary);
 
        host1x_client_iommu_detach(client);
-       host1x_syncpt_free(dc->syncpt);
+       host1x_syncpt_put(dc->syncpt);
 
        return err;
 }
@@ -2172,7 +2239,17 @@ static int tegra_dc_exit(struct host1x_client *client)
        }
 
        host1x_client_iommu_detach(client);
-       host1x_syncpt_free(dc->syncpt);
+       host1x_syncpt_put(dc->syncpt);
+
+       return 0;
+}
+
+static int tegra_dc_late_exit(struct host1x_client *client)
+{
+       struct drm_device *drm = dev_get_drvdata(client->host);
+       struct tegra_drm *tegra = drm->dev_private;
+
+       tegra->num_crtcs--;
 
        return 0;
 }
@@ -2241,8 +2318,10 @@ put_rpm:
 }
 
 static const struct host1x_client_ops dc_client_ops = {
+       .early_init = tegra_dc_early_init,
        .init = tegra_dc_init,
        .exit = tegra_dc_exit,
+       .late_exit = tegra_dc_late_exit,
        .suspend = tegra_dc_runtime_suspend,
        .resume = tegra_dc_runtime_resume,
 };
@@ -2252,6 +2331,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
        .supports_interlacing = false,
        .supports_cursor = false,
        .supports_block_linear = false,
+       .supports_sector_layout = false,
        .has_legacy_blending = true,
        .pitch_align = 8,
        .has_powergate = false,
@@ -2271,6 +2351,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
        .supports_interlacing = false,
        .supports_cursor = false,
        .supports_block_linear = false,
+       .supports_sector_layout = false,
        .has_legacy_blending = true,
        .pitch_align = 8,
        .has_powergate = false,
@@ -2290,6 +2371,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
        .supports_interlacing = false,
        .supports_cursor = false,
        .supports_block_linear = false,
+       .supports_sector_layout = false,
        .has_legacy_blending = true,
        .pitch_align = 64,
        .has_powergate = true,
@@ -2309,6 +2391,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
        .supports_interlacing = true,
        .supports_cursor = true,
        .supports_block_linear = true,
+       .supports_sector_layout = false,
        .has_legacy_blending = false,
        .pitch_align = 64,
        .has_powergate = true,
@@ -2328,6 +2411,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
        .supports_interlacing = true,
        .supports_cursor = true,
        .supports_block_linear = true,
+       .supports_sector_layout = false,
        .has_legacy_blending = false,
        .pitch_align = 64,
        .has_powergate = true,
@@ -2381,6 +2465,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
        .supports_interlacing = true,
        .supports_cursor = true,
        .supports_block_linear = true,
+       .supports_sector_layout = false,
        .has_legacy_blending = false,
        .pitch_align = 64,
        .has_powergate = false,
@@ -2429,6 +2514,7 @@ static const struct tegra_dc_soc_info tegra194_dc_soc_info = {
        .supports_interlacing = true,
        .supports_cursor = true,
        .supports_block_linear = true,
+       .supports_sector_layout = true,
        .has_legacy_blending = false,
        .pitch_align = 64,
        .has_powergate = false,
@@ -2538,9 +2624,16 @@ static int tegra_dc_couple(struct tegra_dc *dc)
 
 static int tegra_dc_probe(struct platform_device *pdev)
 {
+       u64 dma_mask = dma_get_mask(pdev->dev.parent);
        struct tegra_dc *dc;
        int err;
 
+       err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
+               return err;
+       }
+
        dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL);
        if (!dc)
                return -ENOMEM;