From 9d77db3c730780c8ef5ccd4b66403ff5675cfe4e Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 13 May 2024 10:30:14 +0000 Subject: [PATCH] modify sin led gpio --- kernel/drivers/gpu/drm/tegra/dc.c | 420 ++++++++++++++++++++++++++++++++++++++++------------------- 1 files changed, 282 insertions(+), 138 deletions(-) diff --git a/kernel/drivers/gpu/drm/tegra/dc.c b/kernel/drivers/gpu/drm/tegra/dc.c index 03adb4c..958d12d 100644 --- a/kernel/drivers/gpu/drm/tegra/dc.c +++ b/kernel/drivers/gpu/drm/tegra/dc.c @@ -1,20 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Avionic Design GmbH * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/clk.h> #include <linux/debugfs.h> +#include <linux/delay.h> #include <linux/iommu.h> +#include <linux/module.h> #include <linux/of_device.h> #include <linux/pm_runtime.h> #include <linux/reset.h> #include <soc/tegra/pmc.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_debugfs.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_vblank.h> #include "dc.h" #include "drm.h" @@ -22,9 +28,8 @@ #include "hub.h" #include "plane.h" -#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_plane_helper.h> +static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); static void tegra_dc_stats_reset(struct tegra_dc_stats *stats) { @@ -130,7 +135,7 @@ default: WARN_ON_ONCE(1); - /* fallthrough */ + fallthrough; case 4: max = 4; break; @@ -363,6 +368,12 @@ h_size = window->src.w * bpp; v_size = window->src.h; + if (window->reflect_x) + h_offset += (window->src.w - 1) * bpp; + + if (window->reflect_y) + v_offset += window->src.h - 1; + value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size); tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE); @@ -398,9 +409,6 @@ } else { tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE); } - - if (window->bottom_up) - v_offset += window->src.h - 1; tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET); tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET); @@ -465,7 +473,10 @@ value |= COLOR_EXPAND; } - if (window->bottom_up) + if (window->reflect_x) + value |= H_DIRECTION; + + if (window->reflect_y) value |= V_DIRECTION; if (tegra_plane_use_horizontal_filtering(plane, window)) { @@ -596,7 +607,10 @@ struct drm_plane_state *state) { struct tegra_plane_state *plane_state = to_tegra_plane_state(state); - unsigned int rotation = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_Y; + unsigned int supported_rotation = DRM_MODE_ROTATE_0 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y; + unsigned int rotation = state->rotation; struct tegra_bo_tiling *tiling = &plane_state->tiling; struct tegra_plane *tegra = to_tegra_plane(plane); struct tegra_dc *dc = to_tegra_dc(state->crtc); @@ -634,12 +648,26 @@ return -EINVAL; } - rotation = drm_rotation_simplify(state->rotation, rotation); + /* + * Older userspace used custom BO flag in order to specify the Y + * reflection, while modern userspace uses the generic DRM rotation + * property in order to achieve the same result. The legacy BO flag + * duplicates the DRM rotation property when both are set. + */ + if (tegra_fb_is_bottom_up(state->fb)) + rotation |= DRM_MODE_REFLECT_Y; + + rotation = drm_rotation_simplify(rotation, supported_rotation); + + if (rotation & DRM_MODE_REFLECT_X) + plane_state->reflect_x = true; + else + plane_state->reflect_x = false; if (rotation & DRM_MODE_REFLECT_Y) - plane_state->bottom_up = true; + plane_state->reflect_y = true; else - plane_state->bottom_up = false; + plane_state->reflect_y = false; /* * Tegra doesn't support different strides for U and V planes so we @@ -701,7 +729,8 @@ window.dst.w = drm_rect_width(&plane->state->dst); window.dst.h = drm_rect_height(&plane->state->dst); window.bits_per_pixel = fb->format->cpp[0] * 8; - window.bottom_up = tegra_fb_is_bottom_up(fb) || state->bottom_up; + window.reflect_x = state->reflect_x; + window.reflect_y = state->reflect_y; /* copy from state */ window.zpos = plane->state->normalized_zpos; @@ -710,9 +739,7 @@ window.swap = state->swap; for (i = 0; i < fb->format->num_planes; i++) { - struct tegra_bo *bo = tegra_fb_get_plane(fb, i); - - window.base[i] = bo->paddr + fb->offsets[i]; + window.base[i] = state->iova[i] + fb->offsets[i]; /* * Tegra uses a shared stride for UV planes. Framebuffers are @@ -727,6 +754,8 @@ } static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { + .prepare_fb = tegra_plane_prepare_fb, + .cleanup_fb = tegra_plane_cleanup_fb, .atomic_check = tegra_plane_atomic_check, .atomic_disable = tegra_plane_atomic_disable, .atomic_update = tegra_plane_atomic_update, @@ -787,6 +816,8 @@ err = drm_plane_create_rotation_property(&plane->base, DRM_MODE_ROTATE_0, DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_180 | + DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y); if (err < 0) dev_err(dc->dev, "failed to create rotation property: %d\n", @@ -832,16 +863,15 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { - struct tegra_bo *bo = tegra_fb_get_plane(plane->state->fb, 0); + struct tegra_plane_state *state = to_tegra_plane_state(plane->state); struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); - struct drm_plane_state *state = plane->state; u32 value = CURSOR_CLIP_DISPLAY; /* rien ne va plus */ if (!plane->state->crtc || !plane->state->fb) return; - switch (state->crtc_w) { + switch (plane->state->crtc_w) { case 32: value |= CURSOR_SIZE_32x32; break; @@ -859,16 +889,16 @@ break; default: - WARN(1, "cursor size %ux%u not supported\n", state->crtc_w, - state->crtc_h); + WARN(1, "cursor size %ux%u not supported\n", + plane->state->crtc_w, plane->state->crtc_h); return; } - value |= (bo->paddr >> 10) & 0x3fffff; + value |= (state->iova[0] >> 10) & 0x3fffff; tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - value = (bo->paddr >> 32) & 0x3; + value = (state->iova[0] >> 32) & 0x3; tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI); #endif @@ -887,7 +917,8 @@ tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL); /* position the cursor */ - value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff); + value = (plane->state->crtc_y & 0x3fff) << 16 | + (plane->state->crtc_x & 0x3fff); tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION); } @@ -909,9 +940,16 @@ } static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { + .prepare_fb = tegra_plane_prepare_fb, + .cleanup_fb = tegra_plane_cleanup_fb, .atomic_check = tegra_cursor_atomic_check, .atomic_update = tegra_cursor_atomic_update, .atomic_disable = tegra_cursor_atomic_disable, +}; + +static const uint64_t linear_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID }; static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, @@ -942,7 +980,7 @@ err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, - num_formats, NULL, + num_formats, linear_modifiers, DRM_PLANE_TYPE_CURSOR, NULL); if (err < 0) { kfree(plane); @@ -950,6 +988,7 @@ } drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs); + drm_plane_create_zpos_immutable_property(&plane->base, 255); return &plane->base; } @@ -1060,7 +1099,8 @@ err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_plane_funcs, formats, - num_formats, NULL, type, NULL); + num_formats, linear_modifiers, + type, NULL); if (err < 0) { kfree(plane); return ERR_PTR(err); @@ -1072,6 +1112,8 @@ err = drm_plane_create_rotation_property(&plane->base, DRM_MODE_ROTATE_0, DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_180 | + DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y); if (err < 0) dev_err(dc->dev, "failed to create rotation property: %d\n", @@ -1155,21 +1197,12 @@ static void tegra_crtc_reset(struct drm_crtc *crtc) { - struct tegra_dc_state *state; + struct tegra_dc_state *state = kzalloc(sizeof(*state), GFP_KERNEL); if (crtc->state) - __drm_atomic_helper_crtc_destroy_state(crtc->state); + tegra_crtc_atomic_destroy_state(crtc, crtc->state); - kfree(crtc->state); - crtc->state = NULL; - - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (state) { - crtc->state = &state->base; - crtc->state->crtc = crtc; - } - - drm_crtc_vblank_reset(crtc); + __drm_atomic_helper_crtc_reset(crtc, &state->base); } static struct drm_crtc_state * @@ -1497,7 +1530,6 @@ struct drm_minor *minor = crtc->dev->primary; struct dentry *root; struct tegra_dc *dc = to_tegra_dc(crtc); - int err; #ifdef CONFIG_DEBUG_FS root = crtc->debugfs_entry; @@ -1513,17 +1545,9 @@ for (i = 0; i < count; i++) dc->debugfs_files[i].data = dc; - err = drm_debugfs_create_files(dc->debugfs_files, count, root, minor); - if (err < 0) - goto free; + drm_debugfs_create_files(dc->debugfs_files, count, root, minor); return 0; - -free: - kfree(dc->debugfs_files); - dc->debugfs_files = NULL; - - return err; } static void tegra_dc_early_unregister(struct drm_crtc *crtc) @@ -1728,6 +1752,7 @@ { struct tegra_dc *dc = to_tegra_dc(crtc); u32 value; + int err; if (!tegra_dc_idle(dc)) { tegra_dc_stop(dc); @@ -1774,7 +1799,9 @@ spin_unlock_irq(&crtc->dev->event_lock); - pm_runtime_put_sync(dc->dev); + err = host1x_client_suspend(&dc->client); + if (err < 0) + dev_err(dc->dev, "failed to suspend: %d\n", err); } static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, @@ -1784,8 +1811,13 @@ struct tegra_dc_state *state = to_dc_state(crtc->state); struct tegra_dc *dc = to_tegra_dc(crtc); u32 value; + int err; - pm_runtime_get_sync(dc->dev); + err = host1x_client_resume(&dc->client); + if (err < 0) { + dev_err(dc->dev, "failed to resume: %d\n", err); + return; + } /* initialize display controller */ if (dc->syncpt) { @@ -1978,9 +2010,26 @@ return IRQ_HANDLED; } +static bool tegra_dc_has_window_groups(struct tegra_dc *dc) +{ + unsigned int i; + + if (!dc->soc->wgrps) + return true; + + for (i = 0; i < dc->soc->num_wgrps; i++) { + const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i]; + + if (wgrp->dc == dc->pipe && wgrp->num_windows > 0) + return true; + } + + return false; +} + static int tegra_dc_init(struct host1x_client *client) { - struct drm_device *drm = dev_get_drvdata(client->parent); + struct drm_device *drm = dev_get_drvdata(client->host); unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; struct tegra_dc *dc = host1x_client_to_dc(client); struct tegra_drm *tegra = drm->dev_private; @@ -1988,13 +2037,29 @@ struct drm_plane *cursor = NULL; int err; + /* + * 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 + * crash. + */ + if (!tegra_dc_has_window_groups(dc)) + return 0; + + /* + * Set the display hub as the host1x client parent for the display + * controller. This is needed for the runtime reference counting that + * ensures the display hub is always powered when any of the display + * controllers are. + */ + if (dc->soc->has_nvdisplay) + client->parent = &tegra->hub->client; + dc->syncpt = host1x_syncpt_request(client, flags); if (!dc->syncpt) dev_warn(dc->dev, "failed to allocate syncpoint\n"); - dc->group = host1x_client_iommu_attach(client, true); - if (IS_ERR(dc->group)) { - err = PTR_ERR(dc->group); + err = host1x_client_iommu_attach(client); + if (err < 0 && err != -ENODEV) { dev_err(client->dev, "failed to attach to domain: %d\n", err); return err; } @@ -2052,6 +2117,12 @@ goto cleanup; } + /* + * Inherit the DMA parameters (such as maximum segment size) from the + * parent host1x device. + */ + client->dev->dma_parms = client->host->dma_parms; + return 0; cleanup: @@ -2061,7 +2132,7 @@ if (!IS_ERR(primary)) drm_plane_cleanup(primary); - host1x_client_iommu_detach(client, dc->group); + host1x_client_iommu_detach(client); host1x_syncpt_free(dc->syncpt); return err; @@ -2072,6 +2143,12 @@ struct tegra_dc *dc = host1x_client_to_dc(client); int err; + if (!tegra_dc_has_window_groups(dc)) + return 0; + + /* avoid a dangling pointer just in case this disappears */ + client->dev->dma_parms = NULL; + devm_free_irq(dc->dev, dc->irq, dc); err = tegra_dc_rgb_exit(dc); @@ -2080,15 +2157,80 @@ return err; } - host1x_client_iommu_detach(client, dc->group); + host1x_client_iommu_detach(client); host1x_syncpt_free(dc->syncpt); return 0; } +static int tegra_dc_runtime_suspend(struct host1x_client *client) +{ + struct tegra_dc *dc = host1x_client_to_dc(client); + struct device *dev = client->dev; + int err; + + err = reset_control_assert(dc->rst); + if (err < 0) { + dev_err(dev, "failed to assert reset: %d\n", err); + return err; + } + + if (dc->soc->has_powergate) + tegra_powergate_power_off(dc->powergate); + + clk_disable_unprepare(dc->clk); + pm_runtime_put_sync(dev); + + return 0; +} + +static int tegra_dc_runtime_resume(struct host1x_client *client) +{ + struct tegra_dc *dc = host1x_client_to_dc(client); + struct device *dev = client->dev; + int err; + + err = pm_runtime_resume_and_get(dev); + if (err < 0) { + dev_err(dev, "failed to get runtime PM: %d\n", err); + return err; + } + + if (dc->soc->has_powergate) { + err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, + dc->rst); + if (err < 0) { + dev_err(dev, "failed to power partition: %d\n", err); + goto put_rpm; + } + } else { + err = clk_prepare_enable(dc->clk); + if (err < 0) { + dev_err(dev, "failed to enable clock: %d\n", err); + goto put_rpm; + } + + err = reset_control_deassert(dc->rst); + if (err < 0) { + dev_err(dev, "failed to deassert reset: %d\n", err); + goto disable_clk; + } + } + + return 0; + +disable_clk: + clk_disable_unprepare(dc->clk); +put_rpm: + pm_runtime_put_sync(dev); + return err; +} + static const struct host1x_client_ops dc_client_ops = { .init = tegra_dc_init, .exit = tegra_dc_exit, + .suspend = tegra_dc_runtime_suspend, + .resume = tegra_dc_runtime_resume, }; static const struct tegra_dc_soc_info tegra20_dc_soc_info = { @@ -2234,8 +2376,59 @@ .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps), }; +static const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = { + { + .index = 0, + .dc = 0, + .windows = (const unsigned int[]) { 0 }, + .num_windows = 1, + }, { + .index = 1, + .dc = 1, + .windows = (const unsigned int[]) { 1 }, + .num_windows = 1, + }, { + .index = 2, + .dc = 1, + .windows = (const unsigned int[]) { 2 }, + .num_windows = 1, + }, { + .index = 3, + .dc = 2, + .windows = (const unsigned int[]) { 3 }, + .num_windows = 1, + }, { + .index = 4, + .dc = 2, + .windows = (const unsigned int[]) { 4 }, + .num_windows = 1, + }, { + .index = 5, + .dc = 2, + .windows = (const unsigned int[]) { 5 }, + .num_windows = 1, + }, +}; + +static const struct tegra_dc_soc_info tegra194_dc_soc_info = { + .supports_background_color = true, + .supports_interlacing = true, + .supports_cursor = true, + .supports_block_linear = true, + .has_legacy_blending = false, + .pitch_align = 64, + .has_powergate = false, + .coupled_pm = false, + .has_nvdisplay = true, + .wgrps = tegra194_dc_wgrps, + .num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps), +}; + static const struct of_device_id tegra_dc_of_match[] = { { + .compatible = "nvidia,tegra194-dc", + .data = &tegra194_dc_soc_info, + }, { .compatible = "nvidia,tegra186-dc", .data = &tegra186_dc_soc_info, }, { @@ -2296,10 +2489,10 @@ return 0; } -static int tegra_dc_match_by_pipe(struct device *dev, void *data) +static int tegra_dc_match_by_pipe(struct device *dev, const void *data) { struct tegra_dc *dc = dev_get_drvdata(dev); - unsigned int pipe = (unsigned long)data; + unsigned int pipe = (unsigned long)(void *)data; return dc->pipe == pipe; } @@ -2312,22 +2505,18 @@ * POWER_CONTROL registers during CRTC enabling. */ if (dc->soc->coupled_pm && dc->pipe == 1) { - u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER; - struct device_link *link; - struct device *partner; + struct device *companion; + struct tegra_dc *parent; - partner = driver_find_device(dc->dev->driver, NULL, NULL, - tegra_dc_match_by_pipe); - if (!partner) + companion = driver_find_device(dc->dev->driver, NULL, (const void *)0, + tegra_dc_match_by_pipe); + if (!companion) return -EPROBE_DEFER; - link = device_link_add(dc->dev, partner, flags); - if (!link) { - dev_err(dc->dev, "failed to link controllers\n"); - return -EINVAL; - } + parent = dev_get_drvdata(companion); + dc->client.parent = &parent->client; - dev_dbg(dc->dev, "coupled to %s\n", dev_name(partner)); + dev_dbg(dc->dev, "coupled to %s\n", dev_name(companion)); } return 0; @@ -2335,7 +2524,6 @@ static int tegra_dc_probe(struct platform_device *pdev) { - struct resource *regs; struct tegra_dc *dc; int err; @@ -2376,8 +2564,10 @@ usleep_range(2000, 4000); err = reset_control_assert(dc->rst); - if (err < 0) + if (err < 0) { + clk_disable_unprepare(dc->clk); return err; + } usleep_range(2000, 4000); @@ -2392,20 +2582,23 @@ tegra_powergate_power_off(dc->powergate); } - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dc->regs = devm_ioremap_resource(&pdev->dev, regs); + dc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dc->regs)) return PTR_ERR(dc->regs); dc->irq = platform_get_irq(pdev, 0); - if (dc->irq < 0) { - dev_err(&pdev->dev, "failed to get IRQ\n"); + if (dc->irq < 0) return -ENXIO; - } err = tegra_dc_rgb_probe(dc); if (err < 0 && err != -ENODEV) { - dev_err(&pdev->dev, "failed to probe RGB output: %d\n", err); + const char *level = KERN_ERR; + + if (err == -EPROBE_DEFER) + level = KERN_DEBUG; + + dev_printk(level, dc->dev, "failed to probe RGB output: %d\n", + err); return err; } @@ -2420,10 +2613,16 @@ if (err < 0) { dev_err(&pdev->dev, "failed to register host1x client: %d\n", err); - return err; + goto disable_pm; } return 0; + +disable_pm: + pm_runtime_disable(&pdev->dev); + tegra_dc_rgb_remove(dc); + + return err; } static int tegra_dc_remove(struct platform_device *pdev) @@ -2449,65 +2648,10 @@ return 0; } -#ifdef CONFIG_PM -static int tegra_dc_suspend(struct device *dev) -{ - struct tegra_dc *dc = dev_get_drvdata(dev); - int err; - - err = reset_control_assert(dc->rst); - if (err < 0) { - dev_err(dev, "failed to assert reset: %d\n", err); - return err; - } - - if (dc->soc->has_powergate) - tegra_powergate_power_off(dc->powergate); - - clk_disable_unprepare(dc->clk); - - return 0; -} - -static int tegra_dc_resume(struct device *dev) -{ - struct tegra_dc *dc = dev_get_drvdata(dev); - int err; - - if (dc->soc->has_powergate) { - err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, - dc->rst); - if (err < 0) { - dev_err(dev, "failed to power partition: %d\n", err); - return err; - } - } else { - err = clk_prepare_enable(dc->clk); - if (err < 0) { - dev_err(dev, "failed to enable clock: %d\n", err); - return err; - } - - err = reset_control_deassert(dc->rst); - if (err < 0) { - dev_err(dev, "failed to deassert reset: %d\n", err); - return err; - } - } - - return 0; -} -#endif - -static const struct dev_pm_ops tegra_dc_pm_ops = { - SET_RUNTIME_PM_OPS(tegra_dc_suspend, tegra_dc_resume, NULL) -}; - struct platform_driver tegra_dc_driver = { .driver = { .name = "tegra-dc", .of_match_table = tegra_dc_of_match, - .pm = &tegra_dc_pm_ops, }, .probe = tegra_dc_probe, .remove = tegra_dc_remove, -- Gitblit v1.6.2