Merge tag 'drm-misc-next-2018-02-13' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 4.17: UAPI Changes: - drm/vc4: Expose performance counters to userspace (Boris) Cross-subsystem Changes: - MAINTAINERS: Linus to maintain panel-arm-versatile in -misc (Linus) Core Changes: - Only use swiotlb when necessary (Chunming) Driver Changes: - drm/panel: Add support for ARM Versatile panels (Linus) - pl111: Improvements around versatile panel support (Linus) ---------------------------------------- Tagged on 2018-02-06: drm-misc-next for 4.17: UAPI Changes: - Validate mode flags + type (Ville) - Deprecate unused mode flags PIXMUX, BCAST (Ville) - Deprecate unused mode types BUILTIN, CRTC_C, CLOCK_C, DEFAULT (Ville) Cross-subsystem Changes: - MAINTAINERS: s/Daniel/Maarten/ for drm-misc (Daniel) Core Changes: - gem: Export gem functions for drivers to use (Samuel) - bridge: Introduce bridge timings in drm_bridge (Linus) - dma-buf: Allow exclusive fence to be bundled in fence array when calling reservation_object_get_fences_rcu (Christian) - dp: Add training pattern 4 and HBR3 support to dp helpers (Manasi) - fourcc: Add alpha bit to formats to avoid driver format LUTs (Maxime) - mode: Various cleanups + add new device-wide .mode_valid hook (Ville) - atomic: Fix state leak when non-blocking commits fail (Leo) NOTE: IIRC, this was cross-picked to -fixes so it might fall out - crc: Allow polling on the data fd (Maarten) Driver Changes: - bridge/vga-dac: Add THS8134* support (Linus) - tinydrm: Various MIPI DBI improvements/cleanups (Noralf) - bridge/dw-mipi-dsi: Cleanups + use create_packet helper (Brian) - drm/sun4i: Add Display Engine frontend support (Maxime) - drm/sun4i: Add zpos support + increase num planes from 2 to 4 (Maxime) - various: Use drm_mode_get_hv_timing() to fill plane clip rectangle (Ville) - stm: Add 8-bit clut support, add dsi phy v1.31 support, +fixes (Phillipe) Cc: Boris Brezillon <boris.brezillon@free-electrons.com> Cc: Chunming Zhou <david1.zhou@amd.com> Cc: Samuel Li <Samuel.Li@amd.com> Cc: Linus Walleij <linus.walleij@linaro.org> Cc: Noralf Trønnes <noralf@tronnes.org> Cc: Brian Norris <briannorris@chromium.org> Cc: Maxime Ripard <maxime.ripard@free-electrons.com> Cc: Ville Syrjala <ville.syrjala@linux.intel.com> Cc: Christian König <christian.koenig@amd.com> Cc: Manasi Navare <manasi.d.navare@intel.com> Cc: Philippe Cornu <philippe.cornu@st.com> Cc: Leo (Sunpeng) Li <sunpeng.li@amd.com> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> * tag 'drm-misc-next-2018-02-13' of git://anongit.freedesktop.org/drm/drm-misc: (115 commits) drm/radeon: only enable swiotlb path when need v2 drm/amdgpu: only enable swiotlb alloc when need v2 drm: add func to get max iomem address v2 drm/vc4: Expose performance counters to userspace drm: Print the pid when debug logging an ioctl error. drm/stm: ltdc: remove non-alpha color formats on layer 2 for older hw drm/stm: ltdc: add non-alpha color formats drm/bridge/synopsys: dsi: Add 1.31 version support drm/bridge/synopsys: dsi: Add read feature drm/pl111: Support multiple endpoints on the CLCD drm/pl111: Support variants with broken VBLANK drm/pl111: Support variants with broken clock divider drm/pl111: Handle the Versatile RGB/BGR565 mode drm/pl111: Properly detect the ARM PL110 variants drm/panel: Add support for ARM Versatile panels drm/panel: Device tree bindings for ARM Versatile panels drm/bridge: Rename argument from crtc to bridge drm/crc: Add support for polling on the data fd. drm/sun4i: Use drm_mode_get_hv_timing() to populate plane clip rectangle drm/rcar-du: Use drm_mode_get_hv_timing() to populate plane clip rectangle ...
このコミットが含まれているのは:
@@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
sun4i-backend-y += sun4i_backend.o sun4i_layer.o
|
||||
sun4i-frontend-y += sun4i_frontend.o
|
||||
|
||||
sun4i-drm-y += sun4i_drv.o
|
||||
sun4i-drm-y += sun4i_framebuffer.o
|
||||
@@ -24,6 +25,6 @@ obj-$(CONFIG_DRM_SUN4I) += sun4i-tcon.o
|
||||
obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
|
||||
obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
|
||||
|
||||
obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o
|
||||
obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o sun4i-frontend.o
|
||||
obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o
|
||||
obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o
|
||||
|
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#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>
|
||||
@@ -26,6 +27,7 @@
|
||||
|
||||
#include "sun4i_backend.h"
|
||||
#include "sun4i_drv.h"
|
||||
#include "sun4i_frontend.h"
|
||||
#include "sun4i_layer.h"
|
||||
#include "sunxi_engine.h"
|
||||
|
||||
@@ -93,7 +95,7 @@ void sun4i_backend_layer_enable(struct sun4i_backend *backend,
|
||||
static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane,
|
||||
u32 format, u32 *mode)
|
||||
{
|
||||
if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
|
||||
if (plane && (plane->type == DRM_PLANE_TYPE_PRIMARY) &&
|
||||
(format == DRM_FORMAT_ARGB8888))
|
||||
format = DRM_FORMAT_XRGB8888;
|
||||
|
||||
@@ -141,7 +143,6 @@ 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);
|
||||
|
||||
@@ -153,12 +154,6 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend,
|
||||
state->crtc_h));
|
||||
}
|
||||
|
||||
/* Set the line width */
|
||||
DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8);
|
||||
regmap_write(backend->engine.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);
|
||||
@@ -210,6 +205,30 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sun4i_backend_update_layer_frontend(struct sun4i_backend *backend,
|
||||
int layer, uint32_t fmt)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = sun4i_backend_drm_format_to_layer(NULL, fmt, &val);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DRIVER("Invalid format\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_update_bits(backend->engine.regs,
|
||||
SUN4I_BACKEND_ATTCTL_REG0(layer),
|
||||
SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN,
|
||||
SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN);
|
||||
|
||||
regmap_update_bits(backend->engine.regs,
|
||||
SUN4I_BACKEND_ATTCTL_REG1(layer),
|
||||
SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
|
||||
int layer, struct drm_plane *plane)
|
||||
{
|
||||
@@ -218,6 +237,12 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
|
||||
u32 lo_paddr, hi_paddr;
|
||||
dma_addr_t paddr;
|
||||
|
||||
/* Set the line width */
|
||||
DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8);
|
||||
regmap_write(backend->engine.regs,
|
||||
SUN4I_BACKEND_LAYLINEWIDTH_REG(layer),
|
||||
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);
|
||||
@@ -246,6 +271,176 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sun4i_backend_update_layer_zpos(struct sun4i_backend *backend, int layer,
|
||||
struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
unsigned int priority = state->normalized_zpos;
|
||||
|
||||
DRM_DEBUG_DRIVER("Setting layer %d's priority to %d\n", layer, priority);
|
||||
|
||||
regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_ATTCTL_REG0(layer),
|
||||
SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK,
|
||||
SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(priority));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool sun4i_backend_plane_uses_scaler(struct drm_plane_state *state)
|
||||
{
|
||||
u16 src_h = state->src_h >> 16;
|
||||
u16 src_w = state->src_w >> 16;
|
||||
|
||||
DRM_DEBUG_DRIVER("Input size %dx%d, output size %dx%d\n",
|
||||
src_w, src_h, state->crtc_w, state->crtc_h);
|
||||
|
||||
if ((state->crtc_h != src_h) || (state->crtc_w != src_w))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sun4i_backend_plane_uses_frontend(struct drm_plane_state *state)
|
||||
{
|
||||
struct sun4i_layer *layer = plane_to_sun4i_layer(state->plane);
|
||||
struct sun4i_backend *backend = layer->backend;
|
||||
|
||||
if (IS_ERR(backend->frontend))
|
||||
return false;
|
||||
|
||||
return sun4i_backend_plane_uses_scaler(state);
|
||||
}
|
||||
|
||||
static void sun4i_backend_atomic_begin(struct sunxi_engine *engine,
|
||||
struct drm_crtc_state *old_state)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
WARN_ON(regmap_read_poll_timeout(engine->regs,
|
||||
SUN4I_BACKEND_REGBUFFCTL_REG,
|
||||
val, !(val & SUN4I_BACKEND_REGBUFFCTL_LOADCTL),
|
||||
100, 50000));
|
||||
}
|
||||
|
||||
static int sun4i_backend_atomic_check(struct sunxi_engine *engine,
|
||||
struct drm_crtc_state *crtc_state)
|
||||
{
|
||||
struct drm_atomic_state *state = crtc_state->state;
|
||||
struct drm_device *drm = state->dev;
|
||||
struct drm_plane *plane;
|
||||
unsigned int num_planes = 0;
|
||||
unsigned int num_alpha_planes = 0;
|
||||
unsigned int num_frontend_planes = 0;
|
||||
|
||||
DRM_DEBUG_DRIVER("Starting checking our planes\n");
|
||||
|
||||
if (!crtc_state->planes_changed)
|
||||
return 0;
|
||||
|
||||
drm_for_each_plane_mask(plane, drm, crtc_state->plane_mask) {
|
||||
struct drm_plane_state *plane_state =
|
||||
drm_atomic_get_plane_state(state, plane);
|
||||
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_uses_frontend(plane_state)) {
|
||||
DRM_DEBUG_DRIVER("Using the frontend for plane %d\n",
|
||||
plane->index);
|
||||
|
||||
layer_state->uses_frontend = true;
|
||||
num_frontend_planes++;
|
||||
} else {
|
||||
layer_state->uses_frontend = false;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("Plane FB format is %s\n",
|
||||
drm_get_format_name(fb->format->format,
|
||||
&format_name));
|
||||
if (fb->format->has_alpha)
|
||||
num_alpha_planes++;
|
||||
|
||||
num_planes++;
|
||||
}
|
||||
|
||||
/*
|
||||
* The hardware is a bit unusual here.
|
||||
*
|
||||
* Even though it supports 4 layers, it does the composition
|
||||
* in two separate steps.
|
||||
*
|
||||
* The first one is assigning a layer to one of its two
|
||||
* pipes. If more that 1 layer is assigned to the same pipe,
|
||||
* and if pixels overlaps, the pipe will take the pixel from
|
||||
* the layer with the highest priority.
|
||||
*
|
||||
* The second step is the actual alpha blending, that takes
|
||||
* the two pipes as input, and uses the eventual alpha
|
||||
* component to do the transparency between the two.
|
||||
*
|
||||
* This two steps scenario makes us unable to guarantee a
|
||||
* robust alpha blending between the 4 layers in all
|
||||
* situations, since this means that we need to have one layer
|
||||
* with alpha at the lowest position of our two pipes.
|
||||
*
|
||||
* However, we cannot even do that, since the hardware has a
|
||||
* bug where the lowest plane of the lowest pipe (pipe 0,
|
||||
* priority 0), if it has any alpha, will discard the pixel
|
||||
* entirely and just display the pixels in the background
|
||||
* color (black by default).
|
||||
*
|
||||
* This means that we effectively have only three valid
|
||||
* configurations with alpha, all of them with the alpha being
|
||||
* on pipe1 with the lowest position, which can be 1, 2 or 3
|
||||
* depending on the number of planes and their zpos.
|
||||
*/
|
||||
if (num_alpha_planes > SUN4I_BACKEND_NUM_ALPHA_LAYERS) {
|
||||
DRM_DEBUG_DRIVER("Too many planes with alpha, rejecting...\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (num_frontend_planes > SUN4I_BACKEND_NUM_FRONTEND_LAYERS) {
|
||||
DRM_DEBUG_DRIVER("Too many planes going through the frontend, rejecting\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("State valid with %u planes, %u alpha, %u video\n",
|
||||
num_planes, num_alpha_planes, num_frontend_planes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sun4i_backend_vblank_quirk(struct sunxi_engine *engine)
|
||||
{
|
||||
struct sun4i_backend *backend = engine_to_sun4i_backend(engine);
|
||||
struct sun4i_frontend *frontend = backend->frontend;
|
||||
|
||||
if (!frontend)
|
||||
return;
|
||||
|
||||
/*
|
||||
* In a teardown scenario with the frontend involved, we have
|
||||
* to keep the frontend enabled until the next vblank, and
|
||||
* only then disable it.
|
||||
*
|
||||
* This is due to the fact that the backend will not take into
|
||||
* account the new configuration (with the plane that used to
|
||||
* be fed by the frontend now disabled) until we write to the
|
||||
* commit bit and the hardware fetches the new configuration
|
||||
* during the next vblank.
|
||||
*
|
||||
* So we keep the frontend around in order to prevent any
|
||||
* visual artifacts.
|
||||
*/
|
||||
spin_lock(&backend->frontend_lock);
|
||||
if (backend->frontend_teardown) {
|
||||
sun4i_frontend_exit(frontend);
|
||||
backend->frontend_teardown = false;
|
||||
}
|
||||
spin_unlock(&backend->frontend_lock);
|
||||
};
|
||||
|
||||
static int sun4i_backend_init_sat(struct device *dev) {
|
||||
struct sun4i_backend *backend = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@@ -330,11 +525,43 @@ static int sun4i_backend_of_get_id(struct device_node *node)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TODO: This needs to take multiple pipelines into account */
|
||||
static struct sun4i_frontend *sun4i_backend_find_frontend(struct sun4i_drv *drv,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct device_node *port, *ep, *remote;
|
||||
struct sun4i_frontend *frontend;
|
||||
|
||||
port = of_graph_get_port_by_id(node, 0);
|
||||
if (!port)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
for_each_available_child_of_node(port, ep) {
|
||||
remote = of_graph_get_remote_port_parent(ep);
|
||||
if (!remote)
|
||||
continue;
|
||||
|
||||
/* does this node match any registered engines? */
|
||||
list_for_each_entry(frontend, &drv->frontend_list, list) {
|
||||
if (remote == frontend->node) {
|
||||
of_node_put(remote);
|
||||
of_node_put(port);
|
||||
return frontend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static const struct sunxi_engine_ops sun4i_backend_engine_ops = {
|
||||
.atomic_begin = sun4i_backend_atomic_begin,
|
||||
.atomic_check = sun4i_backend_atomic_check,
|
||||
.commit = sun4i_backend_commit,
|
||||
.layers_init = sun4i_layers_init,
|
||||
.apply_color_correction = sun4i_backend_apply_color_correction,
|
||||
.disable_color_correction = sun4i_backend_disable_color_correction,
|
||||
.vblank_quirk = sun4i_backend_vblank_quirk,
|
||||
};
|
||||
|
||||
static struct regmap_config sun4i_backend_regmap_config = {
|
||||
@@ -360,6 +587,7 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
|
||||
if (!backend)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(dev, backend);
|
||||
spin_lock_init(&backend->frontend_lock);
|
||||
|
||||
backend->engine.node = dev->of_node;
|
||||
backend->engine.ops = &sun4i_backend_engine_ops;
|
||||
@@ -367,6 +595,10 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
|
||||
if (backend->engine.id < 0)
|
||||
return backend->engine.id;
|
||||
|
||||
backend->frontend = sun4i_backend_find_frontend(drv, dev->of_node);
|
||||
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);
|
||||
if (IS_ERR(regs))
|
||||
|
@@ -72,6 +72,7 @@
|
||||
#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_REG0_LAY_VDOEN BIT(1)
|
||||
|
||||
#define SUN4I_BACKEND_ATTCTL_REG1(l) (0x8a0 + (0x4 * (l)))
|
||||
#define SUN4I_BACKEND_ATTCTL_REG1_LAY_HSCAFCT GENMASK(15, 14)
|
||||
@@ -111,7 +112,9 @@
|
||||
#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_IYUVLINEWIDTH_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)))
|
||||
@@ -143,8 +146,13 @@
|
||||
#define SUN4I_BACKEND_HWCCOLORTAB_OFF 0x4c00
|
||||
#define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p)))
|
||||
|
||||
#define SUN4I_BACKEND_NUM_LAYERS 4
|
||||
#define SUN4I_BACKEND_NUM_ALPHA_LAYERS 1
|
||||
#define SUN4I_BACKEND_NUM_FRONTEND_LAYERS 1
|
||||
|
||||
struct sun4i_backend {
|
||||
struct sunxi_engine engine;
|
||||
struct sun4i_frontend *frontend;
|
||||
|
||||
struct reset_control *reset;
|
||||
|
||||
@@ -154,6 +162,10 @@ struct sun4i_backend {
|
||||
|
||||
struct clk *sat_clk;
|
||||
struct reset_control *sat_reset;
|
||||
|
||||
/* Protects against races in the frontend teardown */
|
||||
spinlock_t frontend_lock;
|
||||
bool frontend_teardown;
|
||||
};
|
||||
|
||||
static inline struct sun4i_backend *
|
||||
@@ -170,5 +182,9 @@ 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);
|
||||
int sun4i_backend_update_layer_frontend(struct sun4i_backend *backend,
|
||||
int layer, uint32_t in_fmt);
|
||||
int sun4i_backend_update_layer_zpos(struct sun4i_backend *backend,
|
||||
int layer, struct drm_plane *plane);
|
||||
|
||||
#endif /* _SUN4I_BACKEND_H_ */
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <video/videomode.h>
|
||||
|
||||
#include "sun4i_backend.h"
|
||||
#include "sun4i_crtc.h"
|
||||
#include "sun4i_drv.h"
|
||||
#include "sunxi_engine.h"
|
||||
@@ -46,11 +47,25 @@ static struct drm_encoder *sun4i_crtc_get_encoder(struct drm_crtc *crtc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sun4i_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
|
||||
struct sunxi_engine *engine = scrtc->engine;
|
||||
int ret = 0;
|
||||
|
||||
if (engine && engine->ops && engine->ops->atomic_check)
|
||||
ret = engine->ops->atomic_check(engine, state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_state)
|
||||
{
|
||||
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct sunxi_engine *engine = scrtc->engine;
|
||||
unsigned long flags;
|
||||
|
||||
if (crtc->state->event) {
|
||||
@@ -60,7 +75,10 @@ static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
scrtc->event = crtc->state->event;
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (engine->ops->atomic_begin)
|
||||
engine->ops->atomic_begin(engine, old_state);
|
||||
}
|
||||
|
||||
static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
@@ -125,6 +143,7 @@ static void sun4i_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {
|
||||
.atomic_check = sun4i_crtc_atomic_check,
|
||||
.atomic_begin = sun4i_crtc_atomic_begin,
|
||||
.atomic_flush = sun4i_crtc_atomic_flush,
|
||||
.atomic_enable = sun4i_crtc_atomic_enable,
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include <drm/drm_of.h>
|
||||
|
||||
#include "sun4i_drv.h"
|
||||
#include "sun4i_frontend.h"
|
||||
#include "sun4i_framebuffer.h"
|
||||
#include "sun4i_tcon.h"
|
||||
|
||||
@@ -91,6 +92,7 @@ static int sun4i_drv_bind(struct device *dev)
|
||||
goto free_drm;
|
||||
}
|
||||
drm->dev_private = drv;
|
||||
INIT_LIST_HEAD(&drv->frontend_list);
|
||||
INIT_LIST_HEAD(&drv->engine_list);
|
||||
INIT_LIST_HEAD(&drv->tcon_list);
|
||||
|
||||
@@ -177,6 +179,14 @@ static bool sun4i_drv_node_is_frontend(struct device_node *node)
|
||||
of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend");
|
||||
}
|
||||
|
||||
static bool sun4i_drv_node_is_supported_frontend(struct device_node *node)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_DRM_SUN4I_BACKEND))
|
||||
return !!of_match_node(sun4i_frontend_of_table, node);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sun4i_drv_node_is_tcon(struct device_node *node)
|
||||
{
|
||||
return !!of_match_node(sun4i_tcon_of_table, node);
|
||||
@@ -225,9 +235,11 @@ static int sun4i_drv_add_endpoints(struct device *dev,
|
||||
int count = 0;
|
||||
|
||||
/*
|
||||
* We don't support the frontend for now, so we will never
|
||||
* have a device bound. Just skip over it, but we still want
|
||||
* the rest our pipeline to be added.
|
||||
* The frontend has been disabled in some of our old device
|
||||
* trees. If we find a node that is the frontend and is
|
||||
* disabled, we should just follow through and parse its
|
||||
* child, but without adding it to the component list.
|
||||
* Otherwise, we obviously want to add it to the list.
|
||||
*/
|
||||
if (!sun4i_drv_node_is_frontend(node) &&
|
||||
!of_device_is_available(node))
|
||||
@@ -240,7 +252,14 @@ static int sun4i_drv_add_endpoints(struct device *dev,
|
||||
if (sun4i_drv_node_is_connector(node))
|
||||
return 0;
|
||||
|
||||
if (!sun4i_drv_node_is_frontend(node)) {
|
||||
/*
|
||||
* If the device is either just a regular device, or an
|
||||
* enabled frontend supported by the driver, we add it to our
|
||||
* component list.
|
||||
*/
|
||||
if (!sun4i_drv_node_is_frontend(node) ||
|
||||
(sun4i_drv_node_is_supported_frontend(node) &&
|
||||
of_device_is_available(node))) {
|
||||
/* Add current component */
|
||||
DRM_DEBUG_DRIVER("Adding component %pOF\n", node);
|
||||
drm_of_component_match_add(dev, match, compare_of, node);
|
||||
|
@@ -19,6 +19,7 @@
|
||||
|
||||
struct sun4i_drv {
|
||||
struct list_head engine_list;
|
||||
struct list_head frontend_list;
|
||||
struct list_head tcon_list;
|
||||
};
|
||||
|
||||
|
@@ -10,6 +10,7 @@
|
||||
* the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
@@ -19,13 +20,33 @@
|
||||
#include "sun4i_drv.h"
|
||||
#include "sun4i_framebuffer.h"
|
||||
|
||||
static int sun4i_de_atomic_check(struct drm_device *dev,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_atomic_helper_check_modeset(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_atomic_normalize_zpos(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return drm_atomic_helper_check_planes(dev, state);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs sun4i_de_mode_config_funcs = {
|
||||
.output_poll_changed = drm_fb_helper_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_check = sun4i_de_atomic_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
.fb_create = drm_gem_fb_create,
|
||||
};
|
||||
|
||||
static struct drm_mode_config_helper_funcs sun4i_de_mode_config_helpers = {
|
||||
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
|
||||
};
|
||||
|
||||
int sun4i_framebuffer_init(struct drm_device *drm)
|
||||
{
|
||||
drm_mode_config_reset(drm);
|
||||
@@ -34,6 +55,7 @@ int sun4i_framebuffer_init(struct drm_device *drm)
|
||||
drm->mode_config.max_height = 8192;
|
||||
|
||||
drm->mode_config.funcs = &sun4i_de_mode_config_funcs;
|
||||
drm->mode_config.helper_private = &sun4i_de_mode_config_helpers;
|
||||
|
||||
return drm_fb_cma_fbdev_init(drm, 32, 0);
|
||||
}
|
||||
|
389
drivers/gpu/drm/sun4i/sun4i_frontend.c
ノーマルファイル
389
drivers/gpu/drm/sun4i/sun4i_frontend.c
ノーマルファイル
@@ -0,0 +1,389 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2017 Free Electrons
|
||||
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||
*/
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "sun4i_drv.h"
|
||||
#include "sun4i_frontend.h"
|
||||
|
||||
static const u32 sun4i_frontend_vert_coef[32] = {
|
||||
0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
|
||||
0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
|
||||
0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
|
||||
0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
|
||||
0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
|
||||
0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
|
||||
0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
|
||||
0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
|
||||
};
|
||||
|
||||
static const u32 sun4i_frontend_horz_coef[64] = {
|
||||
0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
|
||||
0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
|
||||
0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
|
||||
0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
|
||||
0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
|
||||
0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
|
||||
0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
|
||||
0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
|
||||
0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
|
||||
0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
|
||||
0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
|
||||
0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
|
||||
0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
|
||||
0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
|
||||
0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
|
||||
0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
|
||||
};
|
||||
|
||||
static void sun4i_frontend_scaler_init(struct sun4i_frontend *frontend)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i),
|
||||
sun4i_frontend_horz_coef[2 * i]);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF0_REG(i),
|
||||
sun4i_frontend_horz_coef[2 * i]);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF1_REG(i),
|
||||
sun4i_frontend_horz_coef[2 * i + 1]);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF1_REG(i),
|
||||
sun4i_frontend_horz_coef[2 * i + 1]);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTCOEF_REG(i),
|
||||
sun4i_frontend_vert_coef[i]);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTCOEF_REG(i),
|
||||
sun4i_frontend_vert_coef[i]);
|
||||
}
|
||||
|
||||
regmap_update_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
|
||||
SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL,
|
||||
SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL);
|
||||
}
|
||||
|
||||
int sun4i_frontend_init(struct sun4i_frontend *frontend)
|
||||
{
|
||||
return pm_runtime_get_sync(frontend->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(sun4i_frontend_init);
|
||||
|
||||
void sun4i_frontend_exit(struct sun4i_frontend *frontend)
|
||||
{
|
||||
pm_runtime_put(frontend->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(sun4i_frontend_exit);
|
||||
|
||||
void sun4i_frontend_update_buffer(struct sun4i_frontend *frontend,
|
||||
struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
dma_addr_t paddr;
|
||||
|
||||
/* Set the line width */
|
||||
DRM_DEBUG_DRIVER("Frontend stride: %d bytes\n", fb->pitches[0]);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD0_REG,
|
||||
fb->pitches[0]);
|
||||
|
||||
/* Set the physical address of the buffer in memory */
|
||||
paddr = drm_fb_cma_get_gem_addr(fb, state, 0);
|
||||
paddr -= PHYS_OFFSET;
|
||||
DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR0_REG, paddr);
|
||||
}
|
||||
EXPORT_SYMBOL(sun4i_frontend_update_buffer);
|
||||
|
||||
static int sun4i_frontend_drm_format_to_input_fmt(uint32_t fmt, u32 *val)
|
||||
{
|
||||
switch (fmt) {
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
*val = 5;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int sun4i_frontend_drm_format_to_output_fmt(uint32_t fmt, u32 *val)
|
||||
{
|
||||
switch (fmt) {
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
*val = 2;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int sun4i_frontend_update_formats(struct sun4i_frontend *frontend,
|
||||
struct drm_plane *plane, uint32_t out_fmt)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
u32 out_fmt_val;
|
||||
u32 in_fmt_val;
|
||||
int ret;
|
||||
|
||||
ret = sun4i_frontend_drm_format_to_input_fmt(fb->format->format,
|
||||
&in_fmt_val);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DRIVER("Invalid input format\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sun4i_frontend_drm_format_to_output_fmt(out_fmt, &out_fmt_val);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DRIVER("Invalid output format\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* I have no idea what this does exactly, but it seems to be
|
||||
* related to the scaler FIR filter phase parameters.
|
||||
*/
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG, 0x400);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG, 0x400);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG, 0x400);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG, 0x400);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG, 0x400);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG, 0x400);
|
||||
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_INPUT_FMT_REG,
|
||||
SUN4I_FRONTEND_INPUT_FMT_DATA_MOD(1) |
|
||||
SUN4I_FRONTEND_INPUT_FMT_DATA_FMT(in_fmt_val) |
|
||||
SUN4I_FRONTEND_INPUT_FMT_PS(1));
|
||||
|
||||
/*
|
||||
* TODO: It look like the A31 and A80 at least will need the
|
||||
* bit 7 (ALPHA_EN) enabled when using a format with alpha (so
|
||||
* ARGB8888).
|
||||
*/
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_OUTPUT_FMT_REG,
|
||||
SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT(out_fmt_val));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(sun4i_frontend_update_formats);
|
||||
|
||||
void sun4i_frontend_update_coord(struct sun4i_frontend *frontend,
|
||||
struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
|
||||
/* Set height and width */
|
||||
DRM_DEBUG_DRIVER("Frontend size W: %u H: %u\n",
|
||||
state->crtc_w, state->crtc_h);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_INSIZE_REG,
|
||||
SUN4I_FRONTEND_INSIZE(state->src_h >> 16,
|
||||
state->src_w >> 16));
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_INSIZE_REG,
|
||||
SUN4I_FRONTEND_INSIZE(state->src_h >> 16,
|
||||
state->src_w >> 16));
|
||||
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_OUTSIZE_REG,
|
||||
SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w));
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_OUTSIZE_REG,
|
||||
SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w));
|
||||
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZFACT_REG,
|
||||
state->src_w / state->crtc_w);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZFACT_REG,
|
||||
state->src_w / state->crtc_w);
|
||||
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTFACT_REG,
|
||||
state->src_h / state->crtc_h);
|
||||
regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTFACT_REG,
|
||||
state->src_h / state->crtc_h);
|
||||
|
||||
regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
|
||||
SUN4I_FRONTEND_FRM_CTRL_REG_RDY,
|
||||
SUN4I_FRONTEND_FRM_CTRL_REG_RDY);
|
||||
}
|
||||
EXPORT_SYMBOL(sun4i_frontend_update_coord);
|
||||
|
||||
int sun4i_frontend_enable(struct sun4i_frontend *frontend)
|
||||
{
|
||||
regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
|
||||
SUN4I_FRONTEND_FRM_CTRL_FRM_START,
|
||||
SUN4I_FRONTEND_FRM_CTRL_FRM_START);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(sun4i_frontend_enable);
|
||||
|
||||
static struct regmap_config sun4i_frontend_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = 0x0a14,
|
||||
};
|
||||
|
||||
static int sun4i_frontend_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sun4i_frontend *frontend;
|
||||
struct drm_device *drm = data;
|
||||
struct sun4i_drv *drv = drm->dev_private;
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
|
||||
frontend = devm_kzalloc(dev, sizeof(*frontend), GFP_KERNEL);
|
||||
if (!frontend)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, frontend);
|
||||
frontend->dev = dev;
|
||||
frontend->node = dev->of_node;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
frontend->regs = devm_regmap_init_mmio(dev, regs,
|
||||
&sun4i_frontend_regmap_config);
|
||||
if (IS_ERR(frontend->regs)) {
|
||||
dev_err(dev, "Couldn't create the frontend regmap\n");
|
||||
return PTR_ERR(frontend->regs);
|
||||
}
|
||||
|
||||
frontend->reset = devm_reset_control_get(dev, NULL);
|
||||
if (IS_ERR(frontend->reset)) {
|
||||
dev_err(dev, "Couldn't get our reset line\n");
|
||||
return PTR_ERR(frontend->reset);
|
||||
}
|
||||
|
||||
frontend->bus_clk = devm_clk_get(dev, "ahb");
|
||||
if (IS_ERR(frontend->bus_clk)) {
|
||||
dev_err(dev, "Couldn't get our bus clock\n");
|
||||
return PTR_ERR(frontend->bus_clk);
|
||||
}
|
||||
|
||||
frontend->mod_clk = devm_clk_get(dev, "mod");
|
||||
if (IS_ERR(frontend->mod_clk)) {
|
||||
dev_err(dev, "Couldn't get our mod clock\n");
|
||||
return PTR_ERR(frontend->mod_clk);
|
||||
}
|
||||
|
||||
frontend->ram_clk = devm_clk_get(dev, "ram");
|
||||
if (IS_ERR(frontend->ram_clk)) {
|
||||
dev_err(dev, "Couldn't get our ram clock\n");
|
||||
return PTR_ERR(frontend->ram_clk);
|
||||
}
|
||||
|
||||
list_add_tail(&frontend->list, &drv->frontend_list);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sun4i_frontend_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct sun4i_frontend *frontend = dev_get_drvdata(dev);
|
||||
|
||||
list_del(&frontend->list);
|
||||
pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static const struct component_ops sun4i_frontend_ops = {
|
||||
.bind = sun4i_frontend_bind,
|
||||
.unbind = sun4i_frontend_unbind,
|
||||
};
|
||||
|
||||
static int sun4i_frontend_probe(struct platform_device *pdev)
|
||||
{
|
||||
return component_add(&pdev->dev, &sun4i_frontend_ops);
|
||||
}
|
||||
|
||||
static int sun4i_frontend_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_del(&pdev->dev, &sun4i_frontend_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_frontend_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sun4i_frontend *frontend = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
clk_set_rate(frontend->mod_clk, 300000000);
|
||||
|
||||
clk_prepare_enable(frontend->bus_clk);
|
||||
clk_prepare_enable(frontend->mod_clk);
|
||||
clk_prepare_enable(frontend->ram_clk);
|
||||
|
||||
ret = reset_control_reset(frontend->reset);
|
||||
if (ret) {
|
||||
dev_err(dev, "Couldn't reset our device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_update_bits(frontend->regs, SUN4I_FRONTEND_EN_REG,
|
||||
SUN4I_FRONTEND_EN_EN,
|
||||
SUN4I_FRONTEND_EN_EN);
|
||||
|
||||
regmap_update_bits(frontend->regs, SUN4I_FRONTEND_BYPASS_REG,
|
||||
SUN4I_FRONTEND_BYPASS_CSC_EN,
|
||||
SUN4I_FRONTEND_BYPASS_CSC_EN);
|
||||
|
||||
sun4i_frontend_scaler_init(frontend);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_frontend_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sun4i_frontend *frontend = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(frontend->ram_clk);
|
||||
clk_disable_unprepare(frontend->mod_clk);
|
||||
clk_disable_unprepare(frontend->bus_clk);
|
||||
|
||||
reset_control_assert(frontend->reset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops sun4i_frontend_pm_ops = {
|
||||
.runtime_resume = sun4i_frontend_runtime_resume,
|
||||
.runtime_suspend = sun4i_frontend_runtime_suspend,
|
||||
};
|
||||
|
||||
const struct of_device_id sun4i_frontend_of_table[] = {
|
||||
{ .compatible = "allwinner,sun8i-a33-display-frontend" },
|
||||
{ }
|
||||
};
|
||||
EXPORT_SYMBOL(sun4i_frontend_of_table);
|
||||
MODULE_DEVICE_TABLE(of, sun4i_frontend_of_table);
|
||||
|
||||
static struct platform_driver sun4i_frontend_driver = {
|
||||
.probe = sun4i_frontend_probe,
|
||||
.remove = sun4i_frontend_remove,
|
||||
.driver = {
|
||||
.name = "sun4i-frontend",
|
||||
.of_match_table = sun4i_frontend_of_table,
|
||||
.pm = &sun4i_frontend_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(sun4i_frontend_driver);
|
||||
|
||||
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
|
||||
MODULE_DESCRIPTION("Allwinner A10 Display Engine Frontend Driver");
|
||||
MODULE_LICENSE("GPL");
|
99
drivers/gpu/drm/sun4i/sun4i_frontend.h
ノーマルファイル
99
drivers/gpu/drm/sun4i/sun4i_frontend.h
ノーマルファイル
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2017 Free Electrons
|
||||
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||
*/
|
||||
|
||||
#ifndef _SUN4I_FRONTEND_H_
|
||||
#define _SUN4I_FRONTEND_H_
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
#define SUN4I_FRONTEND_EN_REG 0x000
|
||||
#define SUN4I_FRONTEND_EN_EN BIT(0)
|
||||
|
||||
#define SUN4I_FRONTEND_FRM_CTRL_REG 0x004
|
||||
#define SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL BIT(23)
|
||||
#define SUN4I_FRONTEND_FRM_CTRL_FRM_START BIT(16)
|
||||
#define SUN4I_FRONTEND_FRM_CTRL_COEF_RDY BIT(1)
|
||||
#define SUN4I_FRONTEND_FRM_CTRL_REG_RDY BIT(0)
|
||||
|
||||
#define SUN4I_FRONTEND_BYPASS_REG 0x008
|
||||
#define SUN4I_FRONTEND_BYPASS_CSC_EN BIT(1)
|
||||
|
||||
#define SUN4I_FRONTEND_BUF_ADDR0_REG 0x020
|
||||
|
||||
#define SUN4I_FRONTEND_LINESTRD0_REG 0x040
|
||||
|
||||
#define SUN4I_FRONTEND_INPUT_FMT_REG 0x04c
|
||||
#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD(mod) ((mod) << 8)
|
||||
#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT(fmt) ((fmt) << 4)
|
||||
#define SUN4I_FRONTEND_INPUT_FMT_PS(ps) (ps)
|
||||
|
||||
#define SUN4I_FRONTEND_OUTPUT_FMT_REG 0x05c
|
||||
#define SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT(fmt) (fmt)
|
||||
|
||||
#define SUN4I_FRONTEND_CH0_INSIZE_REG 0x100
|
||||
#define SUN4I_FRONTEND_INSIZE(h, w) ((((h) - 1) << 16) | (((w) - 1)))
|
||||
|
||||
#define SUN4I_FRONTEND_CH0_OUTSIZE_REG 0x104
|
||||
#define SUN4I_FRONTEND_OUTSIZE(h, w) ((((h) - 1) << 16) | (((w) - 1)))
|
||||
|
||||
#define SUN4I_FRONTEND_CH0_HORZFACT_REG 0x108
|
||||
#define SUN4I_FRONTEND_HORZFACT(i, f) (((i) << 16) | (f))
|
||||
|
||||
#define SUN4I_FRONTEND_CH0_VERTFACT_REG 0x10c
|
||||
#define SUN4I_FRONTEND_VERTFACT(i, f) (((i) << 16) | (f))
|
||||
|
||||
#define SUN4I_FRONTEND_CH0_HORZPHASE_REG 0x110
|
||||
#define SUN4I_FRONTEND_CH0_VERTPHASE0_REG 0x114
|
||||
#define SUN4I_FRONTEND_CH0_VERTPHASE1_REG 0x118
|
||||
|
||||
#define SUN4I_FRONTEND_CH1_INSIZE_REG 0x200
|
||||
#define SUN4I_FRONTEND_CH1_OUTSIZE_REG 0x204
|
||||
#define SUN4I_FRONTEND_CH1_HORZFACT_REG 0x208
|
||||
#define SUN4I_FRONTEND_CH1_VERTFACT_REG 0x20c
|
||||
|
||||
#define SUN4I_FRONTEND_CH1_HORZPHASE_REG 0x210
|
||||
#define SUN4I_FRONTEND_CH1_VERTPHASE0_REG 0x214
|
||||
#define SUN4I_FRONTEND_CH1_VERTPHASE1_REG 0x218
|
||||
|
||||
#define SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i) (0x400 + i * 4)
|
||||
#define SUN4I_FRONTEND_CH0_HORZCOEF1_REG(i) (0x480 + i * 4)
|
||||
#define SUN4I_FRONTEND_CH0_VERTCOEF_REG(i) (0x500 + i * 4)
|
||||
#define SUN4I_FRONTEND_CH1_HORZCOEF0_REG(i) (0x600 + i * 4)
|
||||
#define SUN4I_FRONTEND_CH1_HORZCOEF1_REG(i) (0x680 + i * 4)
|
||||
#define SUN4I_FRONTEND_CH1_VERTCOEF_REG(i) (0x700 + i * 4)
|
||||
|
||||
struct clk;
|
||||
struct device_node;
|
||||
struct drm_plane;
|
||||
struct regmap;
|
||||
struct reset_control;
|
||||
|
||||
struct sun4i_frontend {
|
||||
struct list_head list;
|
||||
struct device *dev;
|
||||
struct device_node *node;
|
||||
|
||||
struct clk *bus_clk;
|
||||
struct clk *mod_clk;
|
||||
struct clk *ram_clk;
|
||||
struct regmap *regs;
|
||||
struct reset_control *reset;
|
||||
};
|
||||
|
||||
extern const struct of_device_id sun4i_frontend_of_table[];
|
||||
|
||||
int sun4i_frontend_init(struct sun4i_frontend *frontend);
|
||||
void sun4i_frontend_exit(struct sun4i_frontend *frontend);
|
||||
int sun4i_frontend_enable(struct sun4i_frontend *frontend);
|
||||
|
||||
void sun4i_frontend_update_buffer(struct sun4i_frontend *frontend,
|
||||
struct drm_plane *plane);
|
||||
void sun4i_frontend_update_coord(struct sun4i_frontend *frontend,
|
||||
struct drm_plane *plane);
|
||||
int sun4i_frontend_update_formats(struct sun4i_frontend *frontend,
|
||||
struct drm_plane *plane, uint32_t out_fmt);
|
||||
|
||||
#endif /* _SUN4I_FRONTEND_H_ */
|
@@ -15,34 +15,107 @@
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "sun4i_backend.h"
|
||||
#include "sun4i_frontend.h"
|
||||
#include "sun4i_layer.h"
|
||||
#include "sunxi_engine.h"
|
||||
|
||||
struct sun4i_plane_desc {
|
||||
enum drm_plane_type type;
|
||||
u8 pipe;
|
||||
const uint32_t *formats;
|
||||
uint32_t nformats;
|
||||
enum drm_plane_type type;
|
||||
u8 pipe;
|
||||
const uint32_t *formats;
|
||||
uint32_t nformats;
|
||||
};
|
||||
|
||||
static void sun4i_backend_layer_reset(struct drm_plane *plane)
|
||||
{
|
||||
struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
|
||||
struct sun4i_layer_state *state;
|
||||
|
||||
if (plane->state) {
|
||||
state = state_to_sun4i_layer_state(plane->state);
|
||||
|
||||
__drm_atomic_helper_plane_destroy_state(&state->state);
|
||||
|
||||
kfree(state);
|
||||
plane->state = NULL;
|
||||
}
|
||||
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (state) {
|
||||
plane->state = &state->state;
|
||||
plane->state->plane = plane;
|
||||
plane->state->zpos = layer->id;
|
||||
}
|
||||
}
|
||||
|
||||
static struct drm_plane_state *
|
||||
sun4i_backend_layer_duplicate_state(struct drm_plane *plane)
|
||||
{
|
||||
struct sun4i_layer_state *orig = state_to_sun4i_layer_state(plane->state);
|
||||
struct sun4i_layer_state *copy;
|
||||
|
||||
copy = kzalloc(sizeof(*copy), GFP_KERNEL);
|
||||
if (!copy)
|
||||
return NULL;
|
||||
|
||||
__drm_atomic_helper_plane_duplicate_state(plane, ©->state);
|
||||
copy->uses_frontend = orig->uses_frontend;
|
||||
|
||||
return ©->state;
|
||||
}
|
||||
|
||||
static void sun4i_backend_layer_destroy_state(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct sun4i_layer_state *s_state = state_to_sun4i_layer_state(state);
|
||||
|
||||
__drm_atomic_helper_plane_destroy_state(state);
|
||||
|
||||
kfree(s_state);
|
||||
}
|
||||
|
||||
static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct sun4i_layer_state *layer_state = state_to_sun4i_layer_state(old_state);
|
||||
struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
|
||||
struct sun4i_backend *backend = layer->backend;
|
||||
|
||||
sun4i_backend_layer_enable(backend, layer->id, false);
|
||||
|
||||
if (layer_state->uses_frontend) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&backend->frontend_lock, flags);
|
||||
backend->frontend_teardown = true;
|
||||
spin_unlock_irqrestore(&backend->frontend_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct sun4i_layer_state *layer_state = state_to_sun4i_layer_state(plane->state);
|
||||
struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
|
||||
struct sun4i_backend *backend = layer->backend;
|
||||
struct sun4i_frontend *frontend = backend->frontend;
|
||||
|
||||
if (layer_state->uses_frontend) {
|
||||
sun4i_frontend_init(frontend);
|
||||
sun4i_frontend_update_coord(frontend, plane);
|
||||
sun4i_frontend_update_buffer(frontend, plane);
|
||||
sun4i_frontend_update_formats(frontend, plane,
|
||||
DRM_FORMAT_ARGB8888);
|
||||
sun4i_backend_update_layer_frontend(backend, layer->id,
|
||||
DRM_FORMAT_ARGB8888);
|
||||
sun4i_frontend_enable(frontend);
|
||||
} else {
|
||||
sun4i_backend_update_layer_formats(backend, layer->id, plane);
|
||||
sun4i_backend_update_layer_buffer(backend, layer->id, plane);
|
||||
}
|
||||
|
||||
sun4i_backend_update_layer_coord(backend, layer->id, plane);
|
||||
sun4i_backend_update_layer_formats(backend, layer->id, plane);
|
||||
sun4i_backend_update_layer_buffer(backend, layer->id, plane);
|
||||
sun4i_backend_update_layer_zpos(backend, layer->id, plane);
|
||||
sun4i_backend_layer_enable(backend, layer->id, true);
|
||||
}
|
||||
|
||||
@@ -52,11 +125,11 @@ static const struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
|
||||
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
||||
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
||||
.atomic_destroy_state = sun4i_backend_layer_destroy_state,
|
||||
.atomic_duplicate_state = sun4i_backend_layer_duplicate_state,
|
||||
.destroy = drm_plane_cleanup,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.reset = drm_atomic_helper_plane_reset,
|
||||
.reset = sun4i_backend_layer_reset,
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
};
|
||||
|
||||
@@ -128,32 +201,12 @@ struct drm_plane **sun4i_layers_init(struct drm_device *drm,
|
||||
struct sun4i_backend *backend = engine_to_sun4i_backend(engine);
|
||||
int i;
|
||||
|
||||
planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes) + 1,
|
||||
/* We need to have a sentinel at the need, hence the overallocation */
|
||||
planes = devm_kcalloc(drm->dev, SUN4I_BACKEND_NUM_LAYERS + 1,
|
||||
sizeof(*planes), GFP_KERNEL);
|
||||
if (!planes)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* The hardware is a bit unusual here.
|
||||
*
|
||||
* Even though it supports 4 layers, it does the composition
|
||||
* in two separate steps.
|
||||
*
|
||||
* The first one is assigning a layer to one of its two
|
||||
* pipes. If more that 1 layer is assigned to the same pipe,
|
||||
* and if pixels overlaps, the pipe will take the pixel from
|
||||
* the layer with the highest priority.
|
||||
*
|
||||
* The second step is the actual alpha blending, that takes
|
||||
* the two pipes as input, and uses the eventual alpha
|
||||
* component to do the transparency between the two.
|
||||
*
|
||||
* This two steps scenario makes us unable to guarantee a
|
||||
* robust alpha blending between the 4 layers in all
|
||||
* situations. So we just expose two layers, one per pipe. On
|
||||
* SoCs that support it, sprites could fill the need for more
|
||||
* layers.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(sun4i_backend_planes); i++) {
|
||||
const struct sun4i_plane_desc *plane = &sun4i_backend_planes[i];
|
||||
struct sun4i_layer *layer;
|
||||
@@ -165,6 +218,8 @@ struct drm_plane **sun4i_layers_init(struct drm_device *drm,
|
||||
return ERR_CAST(layer);
|
||||
};
|
||||
|
||||
drm_plane_create_zpos_immutable_property(&layer->plane, i);
|
||||
|
||||
DRM_DEBUG_DRIVER("Assigning %s plane to pipe %d\n",
|
||||
i ? "overlay" : "primary", plane->pipe);
|
||||
regmap_update_bits(engine->regs, SUN4I_BACKEND_ATTCTL_REG0(i),
|
||||
|
@@ -22,12 +22,23 @@ struct sun4i_layer {
|
||||
int id;
|
||||
};
|
||||
|
||||
struct sun4i_layer_state {
|
||||
struct drm_plane_state state;
|
||||
bool uses_frontend;
|
||||
};
|
||||
|
||||
static inline struct sun4i_layer *
|
||||
plane_to_sun4i_layer(struct drm_plane *plane)
|
||||
{
|
||||
return container_of(plane, struct sun4i_layer, plane);
|
||||
}
|
||||
|
||||
static inline struct sun4i_layer_state *
|
||||
state_to_sun4i_layer_state(struct drm_plane_state *state)
|
||||
{
|
||||
return container_of(state, struct sun4i_layer_state, state);
|
||||
}
|
||||
|
||||
struct drm_plane **sun4i_layers_init(struct drm_device *drm,
|
||||
struct sunxi_engine *engine);
|
||||
|
||||
|
@@ -540,6 +540,7 @@ static irqreturn_t sun4i_tcon_handler(int irq, void *private)
|
||||
struct sun4i_tcon *tcon = private;
|
||||
struct drm_device *drm = tcon->drm;
|
||||
struct sun4i_crtc *scrtc = tcon->crtc;
|
||||
struct sunxi_engine *engine = scrtc->engine;
|
||||
unsigned int status;
|
||||
|
||||
regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &status);
|
||||
@@ -557,6 +558,9 @@ static irqreturn_t sun4i_tcon_handler(int irq, void *private)
|
||||
SUN4I_TCON_GINT0_VBLANK_INT(1),
|
||||
0);
|
||||
|
||||
if (engine->ops->vblank_quirk)
|
||||
engine->ops->vblank_quirk(engine);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@@ -211,7 +211,7 @@ static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
|
||||
struct drm_crtc *crtc = state->crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
int min_scale, max_scale;
|
||||
struct drm_rect clip;
|
||||
struct drm_rect clip = {};
|
||||
|
||||
if (!crtc)
|
||||
return 0;
|
||||
@@ -220,10 +220,9 @@ static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
|
||||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
clip.x1 = 0;
|
||||
clip.y1 = 0;
|
||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
min_scale = DRM_PLANE_HELPER_NO_SCALING;
|
||||
max_scale = DRM_PLANE_HELPER_NO_SCALING;
|
||||
|
@@ -239,7 +239,7 @@ static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
|
||||
struct drm_crtc *crtc = state->crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
int min_scale, max_scale;
|
||||
struct drm_rect clip;
|
||||
struct drm_rect clip = {};
|
||||
|
||||
if (!crtc)
|
||||
return 0;
|
||||
@@ -248,10 +248,9 @@ static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
|
||||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
clip.x1 = 0;
|
||||
clip.y1 = 0;
|
||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||
if (crtc_state->enable)
|
||||
drm_mode_get_hv_timing(&crtc_state->mode,
|
||||
&clip.x2, &clip.y2);
|
||||
|
||||
min_scale = DRM_PLANE_HELPER_NO_SCALING;
|
||||
max_scale = DRM_PLANE_HELPER_NO_SCALING;
|
||||
|
@@ -12,16 +12,106 @@
|
||||
|
||||
struct drm_plane;
|
||||
struct drm_device;
|
||||
struct drm_crtc_state;
|
||||
|
||||
struct sunxi_engine;
|
||||
|
||||
/**
|
||||
* struct sunxi_engine_ops - helper operations for sunXi engines
|
||||
*
|
||||
* These hooks are used by the common part of the DRM driver to
|
||||
* implement the proper behaviour.
|
||||
*/
|
||||
struct sunxi_engine_ops {
|
||||
/**
|
||||
* @atomic_begin:
|
||||
*
|
||||
* This callback allows to prepare our engine for an atomic
|
||||
* update. This is mirroring the
|
||||
* &drm_crtc_helper_funcs.atomic_begin callback, so any
|
||||
* documentation there applies.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*atomic_begin)(struct sunxi_engine *engine,
|
||||
struct drm_crtc_state *old_state);
|
||||
|
||||
/**
|
||||
* @atomic_check:
|
||||
*
|
||||
* This callback allows to validate plane-update related CRTC
|
||||
* constraints specific to engines. This is mirroring the
|
||||
* &drm_crtc_helper_funcs.atomic_check callback, so any
|
||||
* documentation there applies.
|
||||
*
|
||||
* This function is optional.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
* 0 on success or a negative error code.
|
||||
*/
|
||||
int (*atomic_check)(struct sunxi_engine *engine,
|
||||
struct drm_crtc_state *state);
|
||||
|
||||
/**
|
||||
* @commit:
|
||||
*
|
||||
* This callback will trigger the hardware switch to commit
|
||||
* the new configuration that has been setup during the next
|
||||
* vblank period.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*commit)(struct sunxi_engine *engine);
|
||||
|
||||
/**
|
||||
* @layers_init:
|
||||
*
|
||||
* This callback is used to allocate, initialize and register
|
||||
* the layers supported by that engine.
|
||||
*
|
||||
* This function is mandatory.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
* The array of struct drm_plane backing the layers, or an
|
||||
* error pointer on failure.
|
||||
*/
|
||||
struct drm_plane **(*layers_init)(struct drm_device *drm,
|
||||
struct sunxi_engine *engine);
|
||||
|
||||
/**
|
||||
* @apply_color_correction:
|
||||
*
|
||||
* This callback will enable the color correction in the
|
||||
* engine. This is useful only for the composite output.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*apply_color_correction)(struct sunxi_engine *engine);
|
||||
|
||||
/**
|
||||
* @disable_color_correction:
|
||||
*
|
||||
* This callback will stop the color correction in the
|
||||
* engine. This is useful only for the composite output.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*disable_color_correction)(struct sunxi_engine *engine);
|
||||
|
||||
/**
|
||||
* @vblank_quirk:
|
||||
*
|
||||
* This callback is used to implement engine-specific
|
||||
* behaviour part of the VBLANK event. It is run with all the
|
||||
* constraints of an interrupt (can't sleep, all local
|
||||
* interrupts disabled) and therefore should be as fast as
|
||||
* possible.
|
||||
*
|
||||
* This function is optional.
|
||||
*/
|
||||
void (*vblank_quirk)(struct sunxi_engine *engine);
|
||||
};
|
||||
|
||||
/**
|
||||
|
新しいイシューから参照
ユーザーをブロックする