mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 13:44:15 +08:00
efeeaefe9b
Introduces a driver for the LogiCVC display controller, a programmable logic controller optimized for use in Xilinx Zynq-7000 SoCs and other Xilinx FPGAs. The controller is mostly configured at logic synthesis time so only a subset of configuration is left for the driver to handle. The following features are implemented and tested: - LVDS 4-bit interface; - RGB565 pixel formats; - Multiple layers and hardware composition; - Layer-wide alpha mode; The following features are implemented but untested: - Other RGB pixel formats; - Layer framebuffer configuration for version 4; - Lowest-layer used as background color; - Per-pixel alpha mode. The following features are not implemented: - YUV pixel formats; - DVI, LVDS 3-bit, ITU656 and camera link interfaces; - External parallel input for layer; - Color-keying; - LUT-based alpha modes. Additional implementation-specific notes: - Panels are only enabled after the first page flip to avoid flashing a white screen. - Depth used in context of the LogiCVC driver only counts color components to match the definition of the synthesis parameters. Support is implemented for both version 3 and 4 of the controller. With version 3, framebuffers are stored in a dedicated contiguous memory area, with a base address hardcoded for each layer. This requires using a dedicated CMA pool registered at the base address and tweaking a few offset-related registers to try to use any buffer allocated from the pool. This is done on a best-effort basis to have the hardware cope with the DRM framebuffer allocation model and there is no guarantee that each buffer allocated by GEM CMA can be used for any layer. In particular, buffers allocated below the base address for a layer are guaranteed not to be configurable for that layer. See the implementation of logicvc_layer_buffer_find_setup for specifics. Version 4 allows configuring each buffer address directly, which guarantees that any buffer can be configured. Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com> Reviewed-by: Maxime Ripard <mripard@kernel.org> Link: https://patchwork.freedesktop.org/patch/msgid/20220520141555.1429041-2-paul.kocialkowski@bootlin.com
186 lines
4.4 KiB
C
186 lines
4.4 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2019-2022 Bootlin
|
|
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
|
*/
|
|
|
|
#include <drm/drm_print.h>
|
|
|
|
#include "logicvc_drm.h"
|
|
#include "logicvc_layer.h"
|
|
#include "logicvc_of.h"
|
|
|
|
static struct logicvc_of_property_sv logicvc_of_display_interface_sv[] = {
|
|
{ "lvds-4bits", LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS },
|
|
{ "lvds-3bits", LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS },
|
|
{ },
|
|
};
|
|
|
|
static struct logicvc_of_property_sv logicvc_of_display_colorspace_sv[] = {
|
|
{ "rgb", LOGICVC_DISPLAY_COLORSPACE_RGB },
|
|
{ "yuv422", LOGICVC_DISPLAY_COLORSPACE_YUV422 },
|
|
{ "yuv444", LOGICVC_DISPLAY_COLORSPACE_YUV444 },
|
|
{ },
|
|
};
|
|
|
|
static struct logicvc_of_property_sv logicvc_of_layer_colorspace_sv[] = {
|
|
{ "rgb", LOGICVC_LAYER_COLORSPACE_RGB },
|
|
{ "yuv", LOGICVC_LAYER_COLORSPACE_YUV },
|
|
{ },
|
|
};
|
|
|
|
static struct logicvc_of_property_sv logicvc_of_layer_alpha_mode_sv[] = {
|
|
{ "layer", LOGICVC_LAYER_ALPHA_LAYER },
|
|
{ "pixel", LOGICVC_LAYER_ALPHA_PIXEL },
|
|
{ },
|
|
};
|
|
|
|
static struct logicvc_of_property logicvc_of_properties[] = {
|
|
[LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE] = {
|
|
.name = "xylon,display-interface",
|
|
.sv = logicvc_of_display_interface_sv,
|
|
.range = {
|
|
LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS,
|
|
LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS,
|
|
},
|
|
},
|
|
[LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE] = {
|
|
.name = "xylon,display-colorspace",
|
|
.sv = logicvc_of_display_colorspace_sv,
|
|
.range = {
|
|
LOGICVC_DISPLAY_COLORSPACE_RGB,
|
|
LOGICVC_DISPLAY_COLORSPACE_YUV444,
|
|
},
|
|
},
|
|
[LOGICVC_OF_PROPERTY_DISPLAY_DEPTH] = {
|
|
.name = "xylon,display-depth",
|
|
.range = { 8, 24 },
|
|
},
|
|
[LOGICVC_OF_PROPERTY_ROW_STRIDE] = {
|
|
.name = "xylon,row-stride",
|
|
},
|
|
[LOGICVC_OF_PROPERTY_DITHERING] = {
|
|
.name = "xylon,dithering",
|
|
.optional = true,
|
|
},
|
|
[LOGICVC_OF_PROPERTY_BACKGROUND_LAYER] = {
|
|
.name = "xylon,background-layer",
|
|
.optional = true,
|
|
},
|
|
[LOGICVC_OF_PROPERTY_LAYERS_CONFIGURABLE] = {
|
|
.name = "xylon,layers-configurable",
|
|
.optional = true,
|
|
},
|
|
[LOGICVC_OF_PROPERTY_LAYERS_COUNT] = {
|
|
.name = "xylon,layers-count",
|
|
},
|
|
[LOGICVC_OF_PROPERTY_LAYER_DEPTH] = {
|
|
.name = "xylon,layer-depth",
|
|
.range = { 8, 24 },
|
|
},
|
|
[LOGICVC_OF_PROPERTY_LAYER_COLORSPACE] = {
|
|
.name = "xylon,layer-colorspace",
|
|
.sv = logicvc_of_layer_colorspace_sv,
|
|
.range = {
|
|
LOGICVC_LAYER_COLORSPACE_RGB,
|
|
LOGICVC_LAYER_COLORSPACE_RGB,
|
|
},
|
|
},
|
|
[LOGICVC_OF_PROPERTY_LAYER_ALPHA_MODE] = {
|
|
.name = "xylon,layer-alpha-mode",
|
|
.sv = logicvc_of_layer_alpha_mode_sv,
|
|
.range = {
|
|
LOGICVC_LAYER_ALPHA_LAYER,
|
|
LOGICVC_LAYER_ALPHA_PIXEL,
|
|
},
|
|
},
|
|
[LOGICVC_OF_PROPERTY_LAYER_BASE_OFFSET] = {
|
|
.name = "xylon,layer-base-offset",
|
|
},
|
|
[LOGICVC_OF_PROPERTY_LAYER_BUFFER_OFFSET] = {
|
|
.name = "xylon,layer-buffer-offset",
|
|
},
|
|
[LOGICVC_OF_PROPERTY_LAYER_PRIMARY] = {
|
|
.name = "xylon,layer-primary",
|
|
.optional = true,
|
|
},
|
|
};
|
|
|
|
static int logicvc_of_property_sv_value(struct logicvc_of_property_sv *sv,
|
|
const char *string, u32 *value)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
while (sv[i].string) {
|
|
if (!strcmp(sv[i].string, string)) {
|
|
*value = sv[i].value;
|
|
return 0;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
int logicvc_of_property_parse_u32(struct device_node *of_node,
|
|
unsigned int index, u32 *target)
|
|
{
|
|
struct logicvc_of_property *property;
|
|
const char *string;
|
|
u32 value;
|
|
int ret;
|
|
|
|
if (index >= LOGICVC_OF_PROPERTY_MAXIMUM)
|
|
return -EINVAL;
|
|
|
|
property = &logicvc_of_properties[index];
|
|
|
|
if (!property->optional &&
|
|
!of_property_read_bool(of_node, property->name))
|
|
return -ENODEV;
|
|
|
|
if (property->sv) {
|
|
ret = of_property_read_string(of_node, property->name, &string);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = logicvc_of_property_sv_value(property->sv, string,
|
|
&value);
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
ret = of_property_read_u32(of_node, property->name, &value);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (property->range[0] || property->range[1])
|
|
if (value < property->range[0] || value > property->range[1])
|
|
return -ERANGE;
|
|
|
|
*target = value;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void logicvc_of_property_parse_bool(struct device_node *of_node,
|
|
unsigned int index, bool *target)
|
|
{
|
|
struct logicvc_of_property *property;
|
|
|
|
if (index >= LOGICVC_OF_PROPERTY_MAXIMUM) {
|
|
/* Fallback. */
|
|
*target = false;
|
|
return;
|
|
}
|
|
|
|
property = &logicvc_of_properties[index];
|
|
*target = of_property_read_bool(of_node, property->name);
|
|
}
|
|
|
|
bool logicvc_of_node_is_layer(struct device_node *of_node)
|
|
{
|
|
return !of_node_cmp(of_node->name, "layer");
|
|
}
|