From 80588a86afd09c3c132ef7dc2a1ab2fe4c5f9d55 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 8 Feb 2016 13:58:56 +0100 Subject: drm: fb: Add seq_file definition Otherwise, building with DEBUG_FS enabled will trigger a build warning because we're using a structure that has not been declared. Signed-off-by: Maxime Ripard --- include/drm/drm_fb_cma_helper.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h index be62bd321e75..ae49c24fbf50 100644 --- a/include/drm/drm_fb_cma_helper.h +++ b/include/drm/drm_fb_cma_helper.h @@ -24,6 +24,8 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, unsigned int plane); #ifdef CONFIG_DEBUG_FS +struct seq_file; + int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg); #endif -- cgit v1.2.3 From 21a454fd361416489f222f1947e7a5bebfde5cc6 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 30 Oct 2015 14:25:05 +0100 Subject: drm: sun4i: Add DT bindings documentation The display pipeline of the Allwinner A10 is involving several loosely coupled components. Add a documentation for the bindings. Acked-by: Rob Herring Signed-off-by: Maxime Ripard --- .../bindings/display/sunxi/sun4i-drm.txt | 258 +++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt new file mode 100644 index 000000000000..df8f4aeefe4c --- /dev/null +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -0,0 +1,258 @@ +Allwinner A10 Display Pipeline +============================== + +The Allwinner A10 Display pipeline is composed of several components +that are going to be documented below: + +TV Encoder +---------- + +The TV Encoder supports the composite and VGA output. It is one end of +the pipeline. + +Required properties: + - compatible: value should be "allwinner,sun4i-a10-tv-encoder". + - reg: base address and size of memory-mapped region + - clocks: the clocks driving the TV encoder + - resets: phandle to the reset controller driving the encoder + +- ports: A ports node with endpoint definitions as defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. The + first port should be the input endpoint. + +TCON +---- + +The TCON acts as a timing controller for RGB, LVDS and TV interfaces. + +Required properties: + - compatible: value should be "allwinner,sun5i-a13-tcon". + - reg: base address and size of memory-mapped region + - interrupts: interrupt associated to this IP + - clocks: phandles to the clocks feeding the TCON. Three are needed: + - 'ahb': the interface clocks + - 'tcon-ch0': The clock driving the TCON channel 0 + - 'tcon-ch1': The clock driving the TCON channel 1 + - resets: phandles to the reset controllers driving the encoder + - "lcd": the reset line for the TCON channel 0 + + - clock-names: the clock names mentioned above + - reset-names: the reset names mentioned above + - clock-output-names: Name of the pixel clock created + +- ports: A ports node with endpoint definitions as defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. The + first port should be the input endpoint, the second one the output + + The output should have two endpoints. The first is the block + connected to the TCON channel 0 (usually a panel or a bridge), the + second the block connected to the TCON channel 1 (usually the TV + encoder) + + +Display Engine Backend +---------------------- + +The display engine backend exposes layers and sprites to the +system. + +Required properties: + - compatible: value must be one of: + * allwinner,sun5i-a13-display-backend + - reg: base address and size of the memory-mapped region. + - clocks: phandles to the clocks feeding the frontend and backend + * ahb: the backend interface clock + * mod: the backend module clock + * ram: the backend DRAM clock + - clock-names: the clock names mentioned above + - resets: phandles to the reset controllers driving the backend + +- ports: A ports node with endpoint definitions as defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. The + first port should be the input endpoints, the second one the output + +Display Engine Frontend +----------------------- + +The display engine frontend does formats conversion, scaling, +deinterlacing and color space conversion. + +Required properties: + - compatible: value must be one of: + * allwinner,sun5i-a13-display-frontend + - reg: base address and size of the memory-mapped region. + - interrupts: interrupt associated to this IP + - clocks: phandles to the clocks feeding the frontend and backend + * ahb: the backend interface clock + * mod: the backend module clock + * ram: the backend DRAM clock + - clock-names: the clock names mentioned above + - resets: phandles to the reset controllers driving the backend + +- ports: A ports node with endpoint definitions as defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. The + first port should be the input endpoints, the second one the outputs + + +Display Engine Pipeline +----------------------- + +The display engine pipeline (and its entry point, since it can be +either directly the backend or the frontend) is represented as an +extra node. + +Required properties: + - compatible: value must be one of: + * allwinner,sun5i-a13-display-engine + + - allwinner,pipelines: list of phandle to the display engine + frontends available. + +Example: + +panel: panel { + compatible = "olimex,lcd-olinuxino-43-ts"; + #address-cells = <1>; + #size-cells = <0>; + + port { + #address-cells = <1>; + #size-cells = <0>; + + panel_input: endpoint { + remote-endpoint = <&tcon0_out_panel>; + }; + }; +}; + +tve0: tv-encoder@01c0a000 { + compatible = "allwinner,sun4i-a10-tv-encoder"; + reg = <0x01c0a000 0x1000>; + clocks = <&ahb_gates 34>; + resets = <&tcon_ch0_clk 0>; + + port { + #address-cells = <1>; + #size-cells = <0>; + + tve0_in_tcon0: endpoint@0 { + reg = <0>; + remote-endpoint = <&tcon0_out_tve0>; + }; + }; +}; + +tcon0: lcd-controller@1c0c000 { + compatible = "allwinner,sun5i-a13-tcon"; + reg = <0x01c0c000 0x1000>; + interrupts = <44>; + resets = <&tcon_ch0_clk 1>; + reset-names = "lcd"; + clocks = <&ahb_gates 36>, + <&tcon_ch0_clk>, + <&tcon_ch1_clk>; + clock-names = "ahb", + "tcon-ch0", + "tcon-ch1"; + clock-output-names = "tcon-pixel-clock"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + tcon0_in: port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + tcon0_in_be0: endpoint@0 { + reg = <0>; + remote-endpoint = <&be0_out_tcon0>; + }; + }; + + tcon0_out: port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + tcon0_out_panel: endpoint@0 { + reg = <0>; + remote-endpoint = <&panel_input>; + }; + + tcon0_out_tve0: endpoint@1 { + reg = <1>; + remote-endpoint = <&tve0_in_tcon0>; + }; + }; + }; +}; + +fe0: display-frontend@1e00000 { + compatible = "allwinner,sun5i-a13-display-frontend"; + reg = <0x01e00000 0x20000>; + interrupts = <47>; + clocks = <&ahb_gates 46>, <&de_fe_clk>, + <&dram_gates 25>; + clock-names = "ahb", "mod", + "ram"; + resets = <&de_fe_clk>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + fe0_out: port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + fe0_out_be0: endpoint { + remote-endpoint = <&be0_in_fe0>; + }; + }; + }; +}; + +be0: display-backend@1e60000 { + compatible = "allwinner,sun5i-a13-display-backend"; + reg = <0x01e60000 0x10000>; + clocks = <&ahb_gates 44>, <&de_be_clk>, + <&dram_gates 26>; + clock-names = "ahb", "mod", + "ram"; + resets = <&de_be_clk>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + be0_in: port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + be0_in_fe0: endpoint@0 { + reg = <0>; + remote-endpoint = <&fe0_out_be0>; + }; + }; + + be0_out: port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + be0_out_tcon0: endpoint@0 { + reg = <0>; + remote-endpoint = <&tcon0_in_be0>; + }; + }; + }; +}; + +display-engine { + compatible = "allwinner,sun5i-a13-display-engine"; + allwinner,pipelines = <&fe0>; +}; -- cgit v1.2.3 From 9026e0d122ac87d329b76a9b631622b03941af64 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 29 Oct 2015 09:36:23 +0100 Subject: drm: Add Allwinner A10 Display Engine support The Allwinner A10 and subsequent SoCs share the same display pipeline, with variations in the number of controllers (1 or 2), or the presence or not of some output (HDMI, TV, VGA) or not. Add a driver with a limited set of features for now, and we will hopefully support all of them eventually Reviewed-by: Boris Brezillon Signed-off-by: Maxime Ripard --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/sun4i/Kconfig | 14 + drivers/gpu/drm/sun4i/Makefile | 10 + drivers/gpu/drm/sun4i/sun4i_backend.c | 364 ++++++++++++++++++++++ drivers/gpu/drm/sun4i/sun4i_backend.h | 165 ++++++++++ drivers/gpu/drm/sun4i/sun4i_crtc.c | 120 +++++++ drivers/gpu/drm/sun4i/sun4i_crtc.h | 30 ++ drivers/gpu/drm/sun4i/sun4i_dotclock.c | 160 ++++++++++ drivers/gpu/drm/sun4i/sun4i_dotclock.h | 21 ++ drivers/gpu/drm/sun4i/sun4i_drv.c | 334 ++++++++++++++++++++ drivers/gpu/drm/sun4i/sun4i_drv.h | 30 ++ drivers/gpu/drm/sun4i/sun4i_framebuffer.c | 54 ++++ drivers/gpu/drm/sun4i/sun4i_framebuffer.h | 19 ++ drivers/gpu/drm/sun4i/sun4i_layer.c | 161 ++++++++++ drivers/gpu/drm/sun4i/sun4i_layer.h | 30 ++ drivers/gpu/drm/sun4i/sun4i_tcon.c | 502 ++++++++++++++++++++++++++++++ drivers/gpu/drm/sun4i/sun4i_tcon.h | 184 +++++++++++ 18 files changed, 2202 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/sun4i/Kconfig create mode 100644 drivers/gpu/drm/sun4i/Makefile create mode 100644 drivers/gpu/drm/sun4i/sun4i_backend.c create mode 100644 drivers/gpu/drm/sun4i/sun4i_backend.h create mode 100644 drivers/gpu/drm/sun4i/sun4i_crtc.c create mode 100644 drivers/gpu/drm/sun4i/sun4i_crtc.h create mode 100644 drivers/gpu/drm/sun4i/sun4i_dotclock.c create mode 100644 drivers/gpu/drm/sun4i/sun4i_dotclock.h create mode 100644 drivers/gpu/drm/sun4i/sun4i_drv.c create mode 100644 drivers/gpu/drm/sun4i/sun4i_drv.h create mode 100644 drivers/gpu/drm/sun4i/sun4i_framebuffer.c create mode 100644 drivers/gpu/drm/sun4i/sun4i_framebuffer.h create mode 100644 drivers/gpu/drm/sun4i/sun4i_layer.c create mode 100644 drivers/gpu/drm/sun4i/sun4i_layer.h create mode 100644 drivers/gpu/drm/sun4i/sun4i_tcon.c create mode 100644 drivers/gpu/drm/sun4i/sun4i_tcon.h diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 9e4f2f1148e5..cd515029bb49 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -252,6 +252,8 @@ source "drivers/gpu/drm/rcar-du/Kconfig" source "drivers/gpu/drm/shmobile/Kconfig" +source "drivers/gpu/drm/sun4i/Kconfig" + source "drivers/gpu/drm/omapdrm/Kconfig" source "drivers/gpu/drm/tilcdc/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index c338d0444e58..1a26b4eb1ce0 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -1,4 +1,4 @@ -# + # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. @@ -65,6 +65,7 @@ obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-y += omapdrm/ +obj-$(CONFIG_DRM_SUN4I) += sun4i/ obj-y += tilcdc/ obj-$(CONFIG_DRM_QXL) += qxl/ obj-$(CONFIG_DRM_BOCHS) += bochs/ diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig new file mode 100644 index 000000000000..99510e64e91a --- /dev/null +++ b/drivers/gpu/drm/sun4i/Kconfig @@ -0,0 +1,14 @@ +config DRM_SUN4I + tristate "DRM Support for Allwinner A10 Display Engine" + depends on DRM && ARM + depends on ARCH_SUNXI || COMPILE_TEST + select DRM_GEM_CMA_HELPER + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select DRM_PANEL + select REGMAP_MMIO + select VIDEOMODE_HELPERS + help + Choose this option if you have an Allwinner SoC with a + Display Engine. If M is selected the module will be called + sun4i-drm. diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile new file mode 100644 index 000000000000..6df3ef32732d --- /dev/null +++ b/drivers/gpu/drm/sun4i/Makefile @@ -0,0 +1,10 @@ +sun4i-drm-y += sun4i_crtc.o +sun4i-drm-y += sun4i_drv.o +sun4i-drm-y += sun4i_framebuffer.o +sun4i-drm-y += sun4i_layer.o + +sun4i-tcon-y += sun4i_tcon.o +sun4i-tcon-y += sun4i_dotclock.o + +obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o +obj-$(CONFIG_DRM_SUN4I) += sun4i_backend.o diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c new file mode 100644 index 000000000000..f7a15c1a93bf --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2015 Free Electrons + * Copyright (C) 2015 NextThing Co + * + * Maxime Ripard + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sun4i_backend.h" +#include "sun4i_drv.h" + +static u32 sunxi_rgb2yuv_coef[12] = { + 0x00000107, 0x00000204, 0x00000064, 0x00000108, + 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808, + 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808 +}; + +void sun4i_backend_apply_color_correction(struct sun4i_backend *backend) +{ + int i; + + DRM_DEBUG_DRIVER("Applying RGB to YUV color correction\n"); + + /* Set color correction */ + regmap_write(backend->regs, SUN4I_BACKEND_OCCTL_REG, + SUN4I_BACKEND_OCCTL_ENABLE); + + for (i = 0; i < 12; i++) + regmap_write(backend->regs, SUN4I_BACKEND_OCRCOEF_REG(i), + sunxi_rgb2yuv_coef[i]); +} +EXPORT_SYMBOL(sun4i_backend_apply_color_correction); + +void sun4i_backend_disable_color_correction(struct sun4i_backend *backend) +{ + DRM_DEBUG_DRIVER("Disabling color correction\n"); + + /* Disable color correction */ + regmap_update_bits(backend->regs, SUN4I_BACKEND_OCCTL_REG, + SUN4I_BACKEND_OCCTL_ENABLE, 0); +} +EXPORT_SYMBOL(sun4i_backend_disable_color_correction); + +void sun4i_backend_commit(struct sun4i_backend *backend) +{ + DRM_DEBUG_DRIVER("Committing changes\n"); + + regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG, + SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS | + SUN4I_BACKEND_REGBUFFCTL_LOADCTL); +} +EXPORT_SYMBOL(sun4i_backend_commit); + +void sun4i_backend_layer_enable(struct sun4i_backend *backend, + int layer, bool enable) +{ + u32 val; + + DRM_DEBUG_DRIVER("Enabling layer %d\n", layer); + + if (enable) + val = SUN4I_BACKEND_MODCTL_LAY_EN(layer); + else + val = 0; + + regmap_update_bits(backend->regs, SUN4I_BACKEND_MODCTL_REG, + SUN4I_BACKEND_MODCTL_LAY_EN(layer), val); +} +EXPORT_SYMBOL(sun4i_backend_layer_enable); + +static int sun4i_backend_drm_format_to_layer(u32 format, u32 *mode) +{ + switch (format) { + case DRM_FORMAT_ARGB8888: + *mode = SUN4I_BACKEND_LAY_FBFMT_ARGB8888; + break; + + case DRM_FORMAT_XRGB8888: + *mode = SUN4I_BACKEND_LAY_FBFMT_XRGB8888; + break; + + case DRM_FORMAT_RGB888: + *mode = SUN4I_BACKEND_LAY_FBFMT_RGB888; + break; + + default: + return -EINVAL; + } + + return 0; +} + +int sun4i_backend_update_layer_coord(struct sun4i_backend *backend, + int layer, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + + 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->regs, SUN4I_BACKEND_DISSIZE_REG, + SUN4I_BACKEND_DISSIZE(state->crtc_w, + state->crtc_h)); + } + + /* Set the line width */ + DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8); + regmap_write(backend->regs, SUN4I_BACKEND_LAYLINEWIDTH_REG(layer), + fb->pitches[0] * 8); + + /* Set height and width */ + DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", + state->crtc_w, state->crtc_h); + regmap_write(backend->regs, SUN4I_BACKEND_LAYSIZE_REG(layer), + SUN4I_BACKEND_LAYSIZE(state->crtc_w, + state->crtc_h)); + + /* Set base coordinates */ + DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n", + state->crtc_x, state->crtc_y); + regmap_write(backend->regs, SUN4I_BACKEND_LAYCOOR_REG(layer), + SUN4I_BACKEND_LAYCOOR(state->crtc_x, + state->crtc_y)); + + return 0; +} +EXPORT_SYMBOL(sun4i_backend_update_layer_coord); + +int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, + int layer, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + bool interlaced = false; + u32 val; + int ret; + + if (plane->state->crtc) + interlaced = plane->state->crtc->state->adjusted_mode.flags + & DRM_MODE_FLAG_INTERLACE; + + regmap_update_bits(backend->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"); + + ret = sun4i_backend_drm_format_to_layer(fb->pixel_format, &val); + if (ret) { + DRM_DEBUG_DRIVER("Invalid format\n"); + return val; + } + + regmap_update_bits(backend->regs, SUN4I_BACKEND_ATTCTL_REG1(layer), + SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val); + + return 0; +} +EXPORT_SYMBOL(sun4i_backend_update_layer_formats); + +int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, + int layer, struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *gem; + u32 lo_paddr, hi_paddr; + dma_addr_t paddr; + int bpp; + + /* Get the physical address of the buffer in memory */ + gem = drm_fb_cma_get_gem_obj(fb, 0); + + DRM_DEBUG_DRIVER("Using GEM @ 0x%x\n", gem->paddr); + + /* Compute the start of the displayed memory */ + bpp = drm_format_plane_cpp(fb->pixel_format, 0); + paddr = gem->paddr + fb->offsets[0]; + paddr += (state->src_x >> 16) * bpp; + paddr += (state->src_y >> 16) * fb->pitches[0]; + + DRM_DEBUG_DRIVER("Setting buffer address to 0x%x\n", paddr); + + /* Write the 32 lower bits of the address (in bits) */ + lo_paddr = paddr << 3; + DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr); + regmap_write(backend->regs, SUN4I_BACKEND_LAYFB_L32ADD_REG(layer), + lo_paddr); + + /* And the upper bits */ + hi_paddr = paddr >> 29; + DRM_DEBUG_DRIVER("Setting address high bits to 0x%x\n", hi_paddr); + regmap_update_bits(backend->regs, SUN4I_BACKEND_LAYFB_H4ADD_REG, + SUN4I_BACKEND_LAYFB_H4ADD_MSK(layer), + SUN4I_BACKEND_LAYFB_H4ADD(layer, hi_paddr)); + + return 0; +} +EXPORT_SYMBOL(sun4i_backend_update_layer_buffer); + +static struct regmap_config sun4i_backend_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x5800, +}; + +static int sun4i_backend_bind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = data; + struct sun4i_drv *drv = drm->dev_private; + struct sun4i_backend *backend; + struct resource *res; + void __iomem *regs; + int i, ret; + + backend = devm_kzalloc(dev, sizeof(*backend), GFP_KERNEL); + if (!backend) + return -ENOMEM; + dev_set_drvdata(dev, backend); + drv->backend = backend; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) { + dev_err(dev, "Couldn't map the backend registers\n"); + return PTR_ERR(regs); + } + + backend->regs = devm_regmap_init_mmio(dev, regs, + &sun4i_backend_regmap_config); + if (IS_ERR(backend->regs)) { + dev_err(dev, "Couldn't create the backend0 regmap\n"); + return PTR_ERR(backend->regs); + } + + backend->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(backend->reset)) { + dev_err(dev, "Couldn't get our reset line\n"); + return PTR_ERR(backend->reset); + } + + ret = reset_control_deassert(backend->reset); + if (ret) { + dev_err(dev, "Couldn't deassert our reset line\n"); + return ret; + } + + backend->bus_clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(backend->bus_clk)) { + dev_err(dev, "Couldn't get the backend bus clock\n"); + ret = PTR_ERR(backend->bus_clk); + goto err_assert_reset; + } + clk_prepare_enable(backend->bus_clk); + + backend->mod_clk = devm_clk_get(dev, "mod"); + if (IS_ERR(backend->mod_clk)) { + dev_err(dev, "Couldn't get the backend module clock\n"); + ret = PTR_ERR(backend->mod_clk); + goto err_disable_bus_clk; + } + clk_prepare_enable(backend->mod_clk); + + backend->ram_clk = devm_clk_get(dev, "ram"); + if (IS_ERR(backend->ram_clk)) { + dev_err(dev, "Couldn't get the backend RAM clock\n"); + ret = PTR_ERR(backend->ram_clk); + goto err_disable_mod_clk; + } + clk_prepare_enable(backend->ram_clk); + + /* Reset the registers */ + for (i = 0x800; i < 0x1000; i += 4) + regmap_write(backend->regs, i, 0); + + /* Disable registers autoloading */ + regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG, + SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS); + + /* Enable the backend */ + regmap_write(backend->regs, SUN4I_BACKEND_MODCTL_REG, + SUN4I_BACKEND_MODCTL_DEBE_EN | + SUN4I_BACKEND_MODCTL_START_CTL); + + return 0; + +err_disable_mod_clk: + clk_disable_unprepare(backend->mod_clk); +err_disable_bus_clk: + clk_disable_unprepare(backend->bus_clk); +err_assert_reset: + reset_control_assert(backend->reset); + return ret; +} + +static void sun4i_backend_unbind(struct device *dev, struct device *master, + void *data) +{ + struct sun4i_backend *backend = dev_get_drvdata(dev); + + clk_disable_unprepare(backend->ram_clk); + clk_disable_unprepare(backend->mod_clk); + clk_disable_unprepare(backend->bus_clk); + reset_control_assert(backend->reset); +} + +static struct component_ops sun4i_backend_ops = { + .bind = sun4i_backend_bind, + .unbind = sun4i_backend_unbind, +}; + +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) +{ + component_del(&pdev->dev, &sun4i_backend_ops); + + return 0; +} + +static const struct of_device_id sun4i_backend_of_table[] = { + { .compatible = "allwinner,sun5i-a13-display-backend" }, + { } +}; +MODULE_DEVICE_TABLE(of, sun4i_backend_of_table); + +static struct platform_driver sun4i_backend_platform_driver = { + .probe = sun4i_backend_probe, + .remove = sun4i_backend_remove, + .driver = { + .name = "sun4i-backend", + .of_match_table = sun4i_backend_of_table, + }, +}; +module_platform_driver(sun4i_backend_platform_driver); + +MODULE_AUTHOR("Maxime Ripard "); +MODULE_DESCRIPTION("Allwinner A10 Display Backend Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h new file mode 100644 index 000000000000..7070bb3434e5 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2015 Free Electrons + * Copyright (C) 2015 NextThing Co + * + * Maxime Ripard + * + * 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. + */ + +#ifndef _SUN4I_BACKEND_H_ +#define _SUN4I_BACKEND_H_ + +#include +#include +#include + +#define SUN4I_BACKEND_MODCTL_REG 0x800 +#define SUN4I_BACKEND_MODCTL_LINE_SEL BIT(29) +#define SUN4I_BACKEND_MODCTL_ITLMOD_EN BIT(28) +#define SUN4I_BACKEND_MODCTL_OUT_SEL GENMASK(22, 20) +#define SUN4I_BACKEND_MODCTL_OUT_LCD (0 << 20) +#define SUN4I_BACKEND_MODCTL_OUT_FE0 (6 << 20) +#define SUN4I_BACKEND_MODCTL_OUT_FE1 (7 << 20) +#define SUN4I_BACKEND_MODCTL_HWC_EN BIT(16) +#define SUN4I_BACKEND_MODCTL_LAY_EN(l) BIT(8 + l) +#define SUN4I_BACKEND_MODCTL_OCSC_EN BIT(5) +#define SUN4I_BACKEND_MODCTL_DFLK_EN BIT(4) +#define SUN4I_BACKEND_MODCTL_DLP_START_CTL BIT(2) +#define SUN4I_BACKEND_MODCTL_START_CTL BIT(1) +#define SUN4I_BACKEND_MODCTL_DEBE_EN BIT(0) + +#define SUN4I_BACKEND_BACKCOLOR_REG 0x804 +#define SUN4I_BACKEND_BACKCOLOR(r, g, b) (((r) << 16) | ((g) << 8) | (b)) + +#define SUN4I_BACKEND_DISSIZE_REG 0x808 +#define SUN4I_BACKEND_DISSIZE(w, h) (((((h) - 1) & 0xffff) << 16) | \ + (((w) - 1) & 0xffff)) + +#define SUN4I_BACKEND_LAYSIZE_REG(l) (0x810 + (0x4 * (l))) +#define SUN4I_BACKEND_LAYSIZE(w, h) (((((h) - 1) & 0x1fff) << 16) | \ + (((w) - 1) & 0x1fff)) + +#define SUN4I_BACKEND_LAYCOOR_REG(l) (0x820 + (0x4 * (l))) +#define SUN4I_BACKEND_LAYCOOR(x, y) ((((u32)(y) & 0xffff) << 16) | \ + ((u32)(x) & 0xffff)) + +#define SUN4I_BACKEND_LAYLINEWIDTH_REG(l) (0x840 + (0x4 * (l))) + +#define SUN4I_BACKEND_LAYFB_L32ADD_REG(l) (0x850 + (0x4 * (l))) + +#define SUN4I_BACKEND_LAYFB_H4ADD_REG 0x860 +#define SUN4I_BACKEND_LAYFB_H4ADD_MSK(l) GENMASK(3 + ((l) * 8), 0) +#define SUN4I_BACKEND_LAYFB_H4ADD(l, val) ((val) << ((l) * 8)) + +#define SUN4I_BACKEND_REGBUFFCTL_REG 0x870 +#define SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS BIT(1) +#define SUN4I_BACKEND_REGBUFFCTL_LOADCTL BIT(0) + +#define SUN4I_BACKEND_CKMAX_REG 0x880 +#define SUN4I_BACKEND_CKMIN_REG 0x884 +#define SUN4I_BACKEND_CKCFG_REG 0x888 +#define SUN4I_BACKEND_ATTCTL_REG0(l) (0x890 + (0x4 * (l))) +#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK BIT(15) +#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(x) ((x) << 15) +#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK GENMASK(11, 10) +#define SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(x) ((x) << 10) + +#define SUN4I_BACKEND_ATTCTL_REG1(l) (0x8a0 + (0x4 * (l))) +#define SUN4I_BACKEND_ATTCTL_REG1_LAY_HSCAFCT GENMASK(15, 14) +#define SUN4I_BACKEND_ATTCTL_REG1_LAY_WSCAFCT GENMASK(13, 12) +#define SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT GENMASK(11, 8) +#define SUN4I_BACKEND_LAY_FBFMT_1BPP (0 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_2BPP (1 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_4BPP (2 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_8BPP (3 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_RGB655 (4 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_RGB565 (5 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_RGB556 (6 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_ARGB1555 (7 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_RGBA5551 (8 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_XRGB8888 (9 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_ARGB8888 (10 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_RGB888 (11 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_ARGB4444 (12 << 8) +#define SUN4I_BACKEND_LAY_FBFMT_RGBA4444 (13 << 8) + +#define SUN4I_BACKEND_DLCDPCTL_REG 0x8b0 +#define SUN4I_BACKEND_DLCDPFRMBUF_ADDRCTL_REG 0x8b4 +#define SUN4I_BACKEND_DLCDPCOOR_REG0 0x8b8 +#define SUN4I_BACKEND_DLCDPCOOR_REG1 0x8bc + +#define SUN4I_BACKEND_INT_EN_REG 0x8c0 +#define SUN4I_BACKEND_INT_FLAG_REG 0x8c4 +#define SUN4I_BACKEND_REG_LOAD_FINISHED BIT(1) + +#define SUN4I_BACKEND_HWCCTL_REG 0x8d8 +#define SUN4I_BACKEND_HWCFBCTL_REG 0x8e0 +#define SUN4I_BACKEND_WBCTL_REG 0x8f0 +#define SUN4I_BACKEND_WBADD_REG 0x8f4 +#define SUN4I_BACKEND_WBLINEWIDTH_REG 0x8f8 +#define SUN4I_BACKEND_SPREN_REG 0x900 +#define SUN4I_BACKEND_SPRFMTCTL_REG 0x908 +#define SUN4I_BACKEND_SPRALPHACTL_REG 0x90c +#define SUN4I_BACKEND_IYUVCTL_REG 0x920 +#define SUN4I_BACKEND_IYUVADD_REG(c) (0x930 + (0x4 * (c))) +#define SUN4I_BACKEND_IYUVLINEWITDTH_REG(c) (0x940 + (0x4 * (c))) +#define SUN4I_BACKEND_YGCOEF_REG(c) (0x950 + (0x4 * (c))) +#define SUN4I_BACKEND_YGCONS_REG 0x95c +#define SUN4I_BACKEND_URCOEF_REG(c) (0x960 + (0x4 * (c))) +#define SUN4I_BACKEND_URCONS_REG 0x96c +#define SUN4I_BACKEND_VBCOEF_REG(c) (0x970 + (0x4 * (c))) +#define SUN4I_BACKEND_VBCONS_REG 0x97c +#define SUN4I_BACKEND_KSCTL_REG 0x980 +#define SUN4I_BACKEND_KSBKCOLOR_REG 0x984 +#define SUN4I_BACKEND_KSFSTLINEWIDTH_REG 0x988 +#define SUN4I_BACKEND_KSVSCAFCT_REG 0x98c +#define SUN4I_BACKEND_KSHSCACOEF_REG(x) (0x9a0 + (0x4 * (x))) +#define SUN4I_BACKEND_OCCTL_REG 0x9c0 +#define SUN4I_BACKEND_OCCTL_ENABLE BIT(0) + +#define SUN4I_BACKEND_OCRCOEF_REG(x) (0x9d0 + (0x4 * (x))) +#define SUN4I_BACKEND_OCRCONS_REG 0x9dc +#define SUN4I_BACKEND_OCGCOEF_REG(x) (0x9e0 + (0x4 * (x))) +#define SUN4I_BACKEND_OCGCONS_REG 0x9ec +#define SUN4I_BACKEND_OCBCOEF_REG(x) (0x9f0 + (0x4 * (x))) +#define SUN4I_BACKEND_OCBCONS_REG 0x9fc +#define SUN4I_BACKEND_SPRCOORCTL_REG(s) (0xa00 + (0x4 * (s))) +#define SUN4I_BACKEND_SPRATTCTL_REG(s) (0xb00 + (0x4 * (s))) +#define SUN4I_BACKEND_SPRADD_REG(s) (0xc00 + (0x4 * (s))) +#define SUN4I_BACKEND_SPRLINEWIDTH_REG(s) (0xd00 + (0x4 * (s))) + +#define SUN4I_BACKEND_SPRPALTAB_OFF 0x4000 +#define SUN4I_BACKEND_GAMMATAB_OFF 0x4400 +#define SUN4I_BACKEND_HWCPATTERN_OFF 0x4800 +#define SUN4I_BACKEND_HWCCOLORTAB_OFF 0x4c00 +#define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p))) + +struct sun4i_backend { + struct regmap *regs; + + struct reset_control *reset; + + struct clk *bus_clk; + struct clk *mod_clk; + struct clk *ram_clk; +}; + +void sun4i_backend_apply_color_correction(struct sun4i_backend *backend); +void sun4i_backend_disable_color_correction(struct sun4i_backend *backend); + +void sun4i_backend_commit(struct sun4i_backend *backend); + +void sun4i_backend_layer_enable(struct sun4i_backend *backend, + int layer, bool enable); +int sun4i_backend_update_layer_coord(struct sun4i_backend *backend, + int layer, struct drm_plane *plane); +int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, + int layer, struct drm_plane *plane); +int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, + int layer, struct drm_plane *plane); + +#endif /* _SUN4I_BACKEND_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c new file mode 100644 index 000000000000..4182a21f5923 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2015 Free Electrons + * Copyright (C) 2015 NextThing Co + * + * Maxime Ripard + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include