diff options
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_backend.c')
| -rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_backend.c | 199 |
1 files changed, 90 insertions, 109 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index a021bab11a4f..40405a52a073 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -1,30 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2015 Free Electrons * Copyright (C) 2015 NextThing Co * * Maxime Ripard <maxime.ripard@free-electrons.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. */ -#include <drm/drmP.h> -#include <drm/drm_atomic.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> -#include <drm/drm_fb_cma_helper.h> -#include <drm/drm_gem_cma_helper.h> -#include <drm/drm_plane_helper.h> - #include <linux/component.h> #include <linux/list.h> +#include <linux/module.h> #include <linux/of_device.h> #include <linux/of_graph.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> #include <linux/reset.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_crtc.h> +#include <drm/drm_fb_dma_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + #include "sun4i_backend.h" #include "sun4i_drv.h" #include "sun4i_frontend.h" @@ -45,28 +46,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; @@ -91,7 +70,9 @@ static void sun4i_backend_disable_color_correction(struct sunxi_engine *engine) SUN4I_BACKEND_OCCTL_ENABLE, 0); } -static void sun4i_backend_commit(struct sunxi_engine *engine) +static void sun4i_backend_commit(struct sunxi_engine *engine, + struct drm_crtc *crtc, + struct drm_atomic_state *state) { DRM_DEBUG_DRIVER("Committing changes\n"); @@ -163,7 +144,6 @@ static const uint32_t sun4i_backend_formats[] = { DRM_FORMAT_ARGB1555, DRM_FORMAT_ARGB4444, DRM_FORMAT_ARGB8888, - DRM_FORMAT_BGRX8888, DRM_FORMAT_RGB565, DRM_FORMAT_RGB888, DRM_FORMAT_RGBA4444, @@ -196,14 +176,6 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend, DRM_DEBUG_DRIVER("Updating layer %d\n", layer); - if (plane->type == DRM_PLANE_TYPE_PRIMARY) { - DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n", - state->crtc_w, state->crtc_h); - regmap_write(backend->engine.regs, SUN4I_BACKEND_DISSIZE_REG, - SUN4I_BACKEND_DISSIZE(state->crtc_w, - state->crtc_h)); - } - /* Set height and width */ DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", state->crtc_w, state->crtc_h); @@ -245,7 +217,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); @@ -282,7 +255,6 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, { struct drm_plane_state *state = plane->state; struct drm_framebuffer *fb = state->fb; - bool interlaced = false; u32 val; int ret; @@ -290,17 +262,6 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_ATTCTL_REG0(layer), SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN, 0); - if (plane->state->crtc) - interlaced = plane->state->crtc->state->adjusted_mode.flags - & DRM_MODE_FLAG_INTERLACE; - - regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG, - SUN4I_BACKEND_MODCTL_ITLMOD_EN, - interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0); - - DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n", - interlaced ? "on" : "off"); - val = SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA(state->alpha >> 8); if (state->alpha != DRM_BLEND_ALPHA_OPAQUE) val |= SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_EN; @@ -371,7 +332,7 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, struct drm_plane_state *state = plane->state; struct drm_framebuffer *fb = state->fb; u32 lo_paddr, hi_paddr; - dma_addr_t paddr; + dma_addr_t dma_addr; /* Set the line width */ DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8); @@ -380,28 +341,21 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, fb->pitches[0] * 8); /* Get the start of the displayed memory */ - paddr = drm_fb_cma_get_gem_addr(fb, state, 0); - DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); - - /* - * backend DMA accesses DRAM directly, bypassing the system - * bus. As such, the address range is different and the buffer - * address needs to be corrected. - */ - paddr -= PHYS_OFFSET; + dma_addr = drm_fb_dma_get_gem_addr(fb, state, 0); + DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &dma_addr); if (fb->format->is_yuv) - return sun4i_backend_update_yuv_buffer(backend, fb, paddr); + return sun4i_backend_update_yuv_buffer(backend, fb, dma_addr); /* Write the 32 lower bits of the address (in bits) */ - lo_paddr = paddr << 3; + lo_paddr = dma_addr << 3; DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr); regmap_write(backend->engine.regs, SUN4I_BACKEND_LAYFB_L32ADD_REG(layer), lo_paddr); /* And the upper bits */ - hi_paddr = paddr >> 29; + hi_paddr = dma_addr >> 29; DRM_DEBUG_DRIVER("Setting address high bits to 0x%x\n", hi_paddr); regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_LAYFB_H4ADD_REG, SUN4I_BACKEND_LAYFB_H4ADD_MSK(layer), @@ -540,7 +494,6 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, struct sun4i_layer_state *layer_state = state_to_sun4i_layer_state(plane_state); struct drm_framebuffer *fb = plane_state->fb; - struct drm_format_name_buf format_name; if (!sun4i_backend_plane_is_supported(plane_state, &layer_state->uses_frontend)) @@ -557,9 +510,8 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, } } - DRM_DEBUG_DRIVER("Plane FB format is %s\n", - drm_get_format_name(fb->format->format, - &format_name)); + DRM_DEBUG_DRIVER("Plane FB format is %p4cc\n", + &fb->format->format); if (fb->format->has_alpha || (plane_state->alpha != DRM_BLEND_ALPHA_OPAQUE)) num_alpha_planes++; @@ -620,8 +572,7 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, /* We can't have an alpha plane at the lowest position */ if (!backend->quirks->supports_lowest_plane_alpha && - (plane_states[0]->fb->format->has_alpha || - (plane_states[0]->alpha != DRM_BLEND_ALPHA_OPAQUE))) + (plane_states[0]->alpha != DRM_BLEND_ALPHA_OPAQUE)) return -EINVAL; for (i = 1; i < num_planes; i++) { @@ -687,6 +638,25 @@ static void sun4i_backend_vblank_quirk(struct sunxi_engine *engine) spin_unlock(&backend->frontend_lock); }; +static void sun4i_backend_mode_set(struct sunxi_engine *engine, + const struct drm_display_mode *mode) +{ + bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); + + DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n", + mode->hdisplay, mode->vdisplay); + + regmap_write(engine->regs, SUN4I_BACKEND_DISSIZE_REG, + SUN4I_BACKEND_DISSIZE(mode->hdisplay, mode->vdisplay)); + + regmap_update_bits(engine->regs, SUN4I_BACKEND_MODCTL_REG, + SUN4I_BACKEND_MODCTL_ITLMOD_EN, + interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0); + + DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n", + interlaced ? "on" : "off"); +} + static int sun4i_backend_init_sat(struct device *dev) { struct sun4i_backend *backend = dev_get_drvdata(dev); int ret; @@ -742,33 +712,22 @@ static int sun4i_backend_free_sat(struct device *dev) { */ static int sun4i_backend_of_get_id(struct device_node *node) { - struct device_node *port, *ep; - int ret = -EINVAL; + struct device_node *ep, *remote; + struct of_endpoint of_ep; - /* input is port 0 */ - port = of_graph_get_port_by_id(node, 0); - if (!port) + /* Input port is 0, and we want the first endpoint. */ + ep = of_graph_get_endpoint_by_regs(node, 0, -1); + if (!ep) return -EINVAL; - /* try finding an upstream endpoint */ - for_each_available_child_of_node(port, ep) { - struct device_node *remote; - u32 reg; - - remote = of_graph_get_remote_endpoint(ep); - if (!remote) - continue; - - ret = of_property_read_u32(remote, "reg", ®); - if (ret) - continue; - - ret = reg; - } - - of_node_put(port); + remote = of_graph_get_remote_endpoint(ep); + of_node_put(ep); + if (!remote) + return -EINVAL; - return ret; + of_graph_parse_endpoint(remote, &of_ep); + of_node_put(remote); + return of_ep.id; } /* TODO: This needs to take multiple pipelines into account */ @@ -809,9 +768,10 @@ static const struct sunxi_engine_ops sun4i_backend_engine_ops = { .apply_color_correction = sun4i_backend_apply_color_correction, .disable_color_correction = sun4i_backend_disable_color_correction, .vblank_quirk = sun4i_backend_vblank_quirk, + .mode_set = sun4i_backend_mode_set, }; -static struct regmap_config sun4i_backend_regmap_config = { +static const struct regmap_config sun4i_backend_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, @@ -826,7 +786,6 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, struct sun4i_drv *drv = drm->dev_private; struct sun4i_backend *backend; const struct sun4i_backend_quirks *quirks; - struct resource *res; void __iomem *regs; int i, ret; @@ -836,6 +795,19 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, dev_set_drvdata(dev, backend); spin_lock_init(&backend->frontend_lock); + if (of_property_present(dev->of_node, "interconnects")) { + /* + * This assume we have the same DMA constraints for all our the + * devices in our pipeline (all the backends, but also the + * frontends). This sounds bad, but it has always been the case + * for us, and DRM doesn't do per-device allocation either, so + * we would need to fix DRM first... + */ + ret = of_dma_configure(drm->dev, dev->of_node, true); + if (ret) + return ret; + } + backend->engine.node = dev->of_node; backend->engine.ops = &sun4i_backend_engine_ops; backend->engine.id = sun4i_backend_of_get_id(dev->of_node); @@ -846,8 +818,7 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, if (IS_ERR(backend->frontend)) dev_warn(dev, "Couldn't find matching frontend, frontend features disabled\n"); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(dev, res); + regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); @@ -877,6 +848,13 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, ret = PTR_ERR(backend->mod_clk); goto err_disable_bus_clk; } + + ret = clk_set_rate_exclusive(backend->mod_clk, 300000000); + if (ret) { + dev_err(dev, "Couldn't set the module clock frequency\n"); + goto err_disable_bus_clk; + } + clk_prepare_enable(backend->mod_clk); backend->ram_clk = devm_clk_get(dev, "ram"); @@ -953,6 +931,7 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, err_disable_ram_clk: clk_disable_unprepare(backend->ram_clk); err_disable_mod_clk: + clk_rate_exclusive_put(backend->mod_clk); clk_disable_unprepare(backend->mod_clk); err_disable_bus_clk: clk_disable_unprepare(backend->bus_clk); @@ -973,6 +952,7 @@ static void sun4i_backend_unbind(struct device *dev, struct device *master, sun4i_backend_free_sat(dev); clk_disable_unprepare(backend->ram_clk); + clk_rate_exclusive_put(backend->mod_clk); clk_disable_unprepare(backend->mod_clk); clk_disable_unprepare(backend->bus_clk); reset_control_assert(backend->reset); @@ -988,11 +968,9 @@ static int sun4i_backend_probe(struct platform_device *pdev) return component_add(&pdev->dev, &sun4i_backend_ops); } -static int sun4i_backend_remove(struct platform_device *pdev) +static void sun4i_backend_remove(struct platform_device *pdev) { component_del(&pdev->dev, &sun4i_backend_ops); - - return 0; } static const struct sun4i_backend_quirks sun4i_backend_quirks = { @@ -1007,7 +985,6 @@ static const struct sun4i_backend_quirks sun6i_backend_quirks = { static const struct sun4i_backend_quirks sun7i_backend_quirks = { .needs_output_muxing = true, - .supports_lowest_plane_alpha = true, }; static const struct sun4i_backend_quirks sun8i_a33_backend_quirks = { @@ -1035,6 +1012,10 @@ static const struct of_device_id sun4i_backend_of_table[] = { .data = &sun7i_backend_quirks, }, { + .compatible = "allwinner,sun8i-a23-display-backend", + .data = &sun8i_a33_backend_quirks, + }, + { .compatible = "allwinner,sun8i-a33-display-backend", .data = &sun8i_a33_backend_quirks, }, |
