mirror of
https://github.com/torvalds/linux.git
synced 2024-11-29 23:51:37 +00:00
4a83c26a1d
Rename "GEM CMA" helpers to "GEM DMA" helpers - considering the
hierarchy of APIs (mm/cma -> dma -> gem dma) calling them "GEM
DMA" seems to be more applicable.
Besides that, commit e57924d4ae
("drm/doc: Task to rename CMA helpers")
requests to rename the CMA helpers and implies that people seem to be
confused about the naming.
In order to do this renaming the following script was used:
```
#!/bin/bash
DIRS="drivers/gpu include/drm Documentation/gpu"
REGEX_SYM_UPPER="[0-9A-Z_\-]"
REGEX_SYM_LOWER="[0-9a-z_\-]"
REGEX_GREP_UPPER="(${REGEX_SYM_UPPER}*)(GEM)_CMA_(${REGEX_SYM_UPPER}*)"
REGEX_GREP_LOWER="(${REGEX_SYM_LOWER}*)(gem)_cma_(${REGEX_SYM_LOWER}*)"
REGEX_SED_UPPER="s/${REGEX_GREP_UPPER}/\1\2_DMA_\3/g"
REGEX_SED_LOWER="s/${REGEX_GREP_LOWER}/\1\2_dma_\3/g"
# Find all upper case 'CMA' symbols and replace them with 'DMA'.
for ff in $(grep -REHl "${REGEX_GREP_UPPER}" $DIRS)
do
sed -i -E "$REGEX_SED_UPPER" $ff
done
# Find all lower case 'cma' symbols and replace them with 'dma'.
for ff in $(grep -REHl "${REGEX_GREP_LOWER}" $DIRS)
do
sed -i -E "$REGEX_SED_LOWER" $ff
done
# Replace all occurrences of 'CMA' / 'cma' in comments and
# documentation files with 'DMA' / 'dma'.
for ff in $(grep -RiHl " cma " $DIRS)
do
sed -i -E "s/ cma / dma /g" $ff
sed -i -E "s/ CMA / DMA /g" $ff
done
# Rename all 'cma_obj's to 'dma_obj'.
for ff in $(grep -RiHl "cma_obj" $DIRS)
do
sed -i -E "s/cma_obj/dma_obj/g" $ff
done
```
Only a few more manual modifications were needed, e.g. reverting the
following modifications in some DRM Kconfig files
- select CMA if HAVE_DMA_CONTIGUOUS
+ select DMA if HAVE_DMA_CONTIGUOUS
as well as manually picking the occurrences of 'CMA'/'cma' in comments and
documentation which relate to "GEM CMA", but not "FB CMA".
Also drivers/gpu/drm/Makefile was fixed up manually after renaming
drm_gem_cma_helper.c to drm_gem_dma_helper.c.
This patch is compile-time tested building a x86_64 kernel with
`make allyesconfig && make drivers/gpu/drm`.
Acked-by: Sam Ravnborg <sam@ravnborg.org>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Danilo Krummrich <dakr@redhat.com>
Reviewed-by: Liviu Dudau <liviu.dudau@arm.com> #drivers/gpu/drm/arm
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20220802000405.949236-4-dakr@redhat.com
497 lines
11 KiB
C
497 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2019-2022 Bootlin
|
|
* Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
|
*/
|
|
|
|
#include <linux/bitfield.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_reserved_mem.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/types.h>
|
|
|
|
#include <drm/drm_atomic_helper.h>
|
|
#include <drm/drm_drv.h>
|
|
#include <drm/drm_fb_helper.h>
|
|
#include <drm/drm_gem_dma_helper.h>
|
|
#include <drm/drm_print.h>
|
|
|
|
#include "logicvc_crtc.h"
|
|
#include "logicvc_drm.h"
|
|
#include "logicvc_interface.h"
|
|
#include "logicvc_mode.h"
|
|
#include "logicvc_layer.h"
|
|
#include "logicvc_of.h"
|
|
#include "logicvc_regs.h"
|
|
|
|
DEFINE_DRM_GEM_DMA_FOPS(logicvc_drm_fops);
|
|
|
|
static int logicvc_drm_gem_dma_dumb_create(struct drm_file *file_priv,
|
|
struct drm_device *drm_dev,
|
|
struct drm_mode_create_dumb *args)
|
|
{
|
|
struct logicvc_drm *logicvc = logicvc_drm(drm_dev);
|
|
|
|
/* Stride is always fixed to its configuration value. */
|
|
args->pitch = logicvc->config.row_stride * DIV_ROUND_UP(args->bpp, 8);
|
|
|
|
return drm_gem_dma_dumb_create_internal(file_priv, drm_dev, args);
|
|
}
|
|
|
|
static struct drm_driver logicvc_drm_driver = {
|
|
.driver_features = DRIVER_GEM | DRIVER_MODESET |
|
|
DRIVER_ATOMIC,
|
|
|
|
.fops = &logicvc_drm_fops,
|
|
.name = "logicvc-drm",
|
|
.desc = "Xylon LogiCVC DRM driver",
|
|
.date = "20200403",
|
|
.major = 1,
|
|
.minor = 0,
|
|
|
|
DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(logicvc_drm_gem_dma_dumb_create),
|
|
};
|
|
|
|
static struct regmap_config logicvc_drm_regmap_config = {
|
|
.reg_bits = 32,
|
|
.val_bits = 32,
|
|
.reg_stride = 4,
|
|
.name = "logicvc-drm",
|
|
};
|
|
|
|
static irqreturn_t logicvc_drm_irq_handler(int irq, void *data)
|
|
{
|
|
struct logicvc_drm *logicvc = data;
|
|
irqreturn_t ret = IRQ_NONE;
|
|
u32 stat = 0;
|
|
|
|
/* Get pending interrupt sources. */
|
|
regmap_read(logicvc->regmap, LOGICVC_INT_STAT_REG, &stat);
|
|
|
|
/* Clear all pending interrupt sources. */
|
|
regmap_write(logicvc->regmap, LOGICVC_INT_STAT_REG, stat);
|
|
|
|
if (stat & LOGICVC_INT_STAT_V_SYNC) {
|
|
logicvc_crtc_vblank_handler(logicvc);
|
|
ret = IRQ_HANDLED;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int logicvc_drm_config_parse(struct logicvc_drm *logicvc)
|
|
{
|
|
struct drm_device *drm_dev = &logicvc->drm_dev;
|
|
struct device *dev = drm_dev->dev;
|
|
struct device_node *of_node = dev->of_node;
|
|
struct logicvc_drm_config *config = &logicvc->config;
|
|
struct device_node *layers_node;
|
|
int ret;
|
|
|
|
logicvc_of_property_parse_bool(of_node, LOGICVC_OF_PROPERTY_DITHERING,
|
|
&config->dithering);
|
|
logicvc_of_property_parse_bool(of_node,
|
|
LOGICVC_OF_PROPERTY_BACKGROUND_LAYER,
|
|
&config->background_layer);
|
|
logicvc_of_property_parse_bool(of_node,
|
|
LOGICVC_OF_PROPERTY_LAYERS_CONFIGURABLE,
|
|
&config->layers_configurable);
|
|
|
|
ret = logicvc_of_property_parse_u32(of_node,
|
|
LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE,
|
|
&config->display_interface);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = logicvc_of_property_parse_u32(of_node,
|
|
LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE,
|
|
&config->display_colorspace);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = logicvc_of_property_parse_u32(of_node,
|
|
LOGICVC_OF_PROPERTY_DISPLAY_DEPTH,
|
|
&config->display_depth);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = logicvc_of_property_parse_u32(of_node,
|
|
LOGICVC_OF_PROPERTY_ROW_STRIDE,
|
|
&config->row_stride);
|
|
if (ret)
|
|
return ret;
|
|
|
|
layers_node = of_get_child_by_name(of_node, "layers");
|
|
if (!layers_node) {
|
|
drm_err(drm_dev, "Missing non-optional layers node\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
config->layers_count = of_get_child_count(layers_node);
|
|
if (!config->layers_count) {
|
|
drm_err(drm_dev,
|
|
"Missing a non-optional layers children node\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int logicvc_clocks_prepare(struct logicvc_drm *logicvc)
|
|
{
|
|
struct drm_device *drm_dev = &logicvc->drm_dev;
|
|
struct device *dev = drm_dev->dev;
|
|
|
|
struct {
|
|
struct clk **clk;
|
|
char *name;
|
|
bool optional;
|
|
} clocks_map[] = {
|
|
{
|
|
.clk = &logicvc->vclk,
|
|
.name = "vclk",
|
|
.optional = false,
|
|
},
|
|
{
|
|
.clk = &logicvc->vclk2,
|
|
.name = "vclk2",
|
|
.optional = true,
|
|
},
|
|
{
|
|
.clk = &logicvc->lvdsclk,
|
|
.name = "lvdsclk",
|
|
.optional = true,
|
|
},
|
|
{
|
|
.clk = &logicvc->lvdsclkn,
|
|
.name = "lvdsclkn",
|
|
.optional = true,
|
|
},
|
|
};
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(clocks_map); i++) {
|
|
struct clk *clk;
|
|
|
|
clk = devm_clk_get(dev, clocks_map[i].name);
|
|
if (IS_ERR(clk)) {
|
|
if (PTR_ERR(clk) == -ENOENT && clocks_map[i].optional)
|
|
continue;
|
|
|
|
drm_err(drm_dev, "Missing non-optional clock %s\n",
|
|
clocks_map[i].name);
|
|
|
|
ret = PTR_ERR(clk);
|
|
goto error;
|
|
}
|
|
|
|
ret = clk_prepare_enable(clk);
|
|
if (ret) {
|
|
drm_err(drm_dev,
|
|
"Failed to prepare and enable clock %s\n",
|
|
clocks_map[i].name);
|
|
goto error;
|
|
}
|
|
|
|
*clocks_map[i].clk = clk;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
for (i = 0; i < ARRAY_SIZE(clocks_map); i++) {
|
|
if (!*clocks_map[i].clk)
|
|
continue;
|
|
|
|
clk_disable_unprepare(*clocks_map[i].clk);
|
|
*clocks_map[i].clk = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int logicvc_clocks_unprepare(struct logicvc_drm *logicvc)
|
|
{
|
|
struct clk **clocks[] = {
|
|
&logicvc->vclk,
|
|
&logicvc->vclk2,
|
|
&logicvc->lvdsclk,
|
|
&logicvc->lvdsclkn,
|
|
};
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(clocks); i++) {
|
|
if (!*clocks[i])
|
|
continue;
|
|
|
|
clk_disable_unprepare(*clocks[i]);
|
|
*clocks[i] = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct logicvc_drm_caps logicvc_drm_caps[] = {
|
|
{
|
|
.major = 3,
|
|
.layer_address = false,
|
|
},
|
|
{
|
|
.major = 4,
|
|
.layer_address = true,
|
|
},
|
|
{
|
|
.major = 5,
|
|
.layer_address = true,
|
|
},
|
|
};
|
|
|
|
static const struct logicvc_drm_caps *
|
|
logicvc_drm_caps_match(struct logicvc_drm *logicvc)
|
|
{
|
|
struct drm_device *drm_dev = &logicvc->drm_dev;
|
|
const struct logicvc_drm_caps *caps = NULL;
|
|
unsigned int major, minor;
|
|
char level;
|
|
unsigned int i;
|
|
u32 version;
|
|
|
|
regmap_read(logicvc->regmap, LOGICVC_IP_VERSION_REG, &version);
|
|
|
|
major = FIELD_GET(LOGICVC_IP_VERSION_MAJOR_MASK, version);
|
|
minor = FIELD_GET(LOGICVC_IP_VERSION_MINOR_MASK, version);
|
|
level = FIELD_GET(LOGICVC_IP_VERSION_LEVEL_MASK, version) + 'a';
|
|
|
|
for (i = 0; i < ARRAY_SIZE(logicvc_drm_caps); i++) {
|
|
if (logicvc_drm_caps[i].major &&
|
|
logicvc_drm_caps[i].major != major)
|
|
continue;
|
|
|
|
if (logicvc_drm_caps[i].minor &&
|
|
logicvc_drm_caps[i].minor != minor)
|
|
continue;
|
|
|
|
if (logicvc_drm_caps[i].level &&
|
|
logicvc_drm_caps[i].level != level)
|
|
continue;
|
|
|
|
caps = &logicvc_drm_caps[i];
|
|
}
|
|
|
|
drm_info(drm_dev, "LogiCVC version %d.%02d.%c\n", major, minor, level);
|
|
|
|
return caps;
|
|
}
|
|
|
|
static int logicvc_drm_probe(struct platform_device *pdev)
|
|
{
|
|
struct device_node *of_node = pdev->dev.of_node;
|
|
struct device_node *reserved_mem_node;
|
|
struct reserved_mem *reserved_mem = NULL;
|
|
const struct logicvc_drm_caps *caps;
|
|
struct logicvc_drm *logicvc;
|
|
struct device *dev = &pdev->dev;
|
|
struct drm_device *drm_dev;
|
|
struct regmap *regmap = NULL;
|
|
struct resource res;
|
|
void __iomem *base;
|
|
int irq;
|
|
int ret;
|
|
|
|
ret = of_reserved_mem_device_init(dev);
|
|
if (ret && ret != -ENODEV) {
|
|
dev_err(dev, "Failed to init memory region\n");
|
|
goto error_early;
|
|
}
|
|
|
|
reserved_mem_node = of_parse_phandle(of_node, "memory-region", 0);
|
|
if (reserved_mem_node) {
|
|
reserved_mem = of_reserved_mem_lookup(reserved_mem_node);
|
|
of_node_put(reserved_mem_node);
|
|
}
|
|
|
|
/* Get regmap from parent if available. */
|
|
if (of_node->parent)
|
|
regmap = syscon_node_to_regmap(of_node->parent);
|
|
|
|
/* Register our own regmap otherwise. */
|
|
if (IS_ERR_OR_NULL(regmap)) {
|
|
ret = of_address_to_resource(of_node, 0, &res);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to get resource from address\n");
|
|
goto error_reserved_mem;
|
|
}
|
|
|
|
base = devm_ioremap_resource(dev, &res);
|
|
if (IS_ERR(base)) {
|
|
dev_err(dev, "Failed to map I/O base\n");
|
|
ret = PTR_ERR(base);
|
|
goto error_reserved_mem;
|
|
}
|
|
|
|
logicvc_drm_regmap_config.max_register = resource_size(&res) -
|
|
4;
|
|
|
|
regmap = devm_regmap_init_mmio(dev, base,
|
|
&logicvc_drm_regmap_config);
|
|
if (IS_ERR(regmap)) {
|
|
dev_err(dev, "Failed to create regmap for I/O\n");
|
|
ret = PTR_ERR(regmap);
|
|
goto error_reserved_mem;
|
|
}
|
|
}
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq < 0) {
|
|
ret = -ENODEV;
|
|
goto error_reserved_mem;
|
|
}
|
|
|
|
logicvc = devm_drm_dev_alloc(dev, &logicvc_drm_driver,
|
|
struct logicvc_drm, drm_dev);
|
|
if (IS_ERR(logicvc)) {
|
|
ret = PTR_ERR(logicvc);
|
|
goto error_reserved_mem;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, logicvc);
|
|
drm_dev = &logicvc->drm_dev;
|
|
|
|
logicvc->regmap = regmap;
|
|
INIT_LIST_HEAD(&logicvc->layers_list);
|
|
|
|
caps = logicvc_drm_caps_match(logicvc);
|
|
if (!caps) {
|
|
ret = -EINVAL;
|
|
goto error_reserved_mem;
|
|
}
|
|
|
|
logicvc->caps = caps;
|
|
|
|
if (reserved_mem)
|
|
logicvc->reserved_mem_base = reserved_mem->base;
|
|
|
|
ret = logicvc_clocks_prepare(logicvc);
|
|
if (ret) {
|
|
drm_err(drm_dev, "Failed to prepare clocks\n");
|
|
goto error_reserved_mem;
|
|
}
|
|
|
|
ret = devm_request_irq(dev, irq, logicvc_drm_irq_handler, 0,
|
|
dev_name(dev), logicvc);
|
|
if (ret) {
|
|
drm_err(drm_dev, "Failed to request IRQ\n");
|
|
goto error_clocks;
|
|
}
|
|
|
|
ret = logicvc_drm_config_parse(logicvc);
|
|
if (ret && ret != -ENODEV) {
|
|
drm_err(drm_dev, "Failed to parse config\n");
|
|
goto error_clocks;
|
|
}
|
|
|
|
ret = drmm_mode_config_init(drm_dev);
|
|
if (ret) {
|
|
drm_err(drm_dev, "Failed to init mode config\n");
|
|
goto error_clocks;
|
|
}
|
|
|
|
ret = logicvc_layers_init(logicvc);
|
|
if (ret) {
|
|
drm_err(drm_dev, "Failed to initialize layers\n");
|
|
goto error_clocks;
|
|
}
|
|
|
|
ret = logicvc_crtc_init(logicvc);
|
|
if (ret) {
|
|
drm_err(drm_dev, "Failed to initialize CRTC\n");
|
|
goto error_clocks;
|
|
}
|
|
|
|
logicvc_layers_attach_crtc(logicvc);
|
|
|
|
ret = logicvc_interface_init(logicvc);
|
|
if (ret) {
|
|
if (ret != -EPROBE_DEFER)
|
|
drm_err(drm_dev, "Failed to initialize interface\n");
|
|
|
|
goto error_clocks;
|
|
}
|
|
|
|
logicvc_interface_attach_crtc(logicvc);
|
|
|
|
ret = logicvc_mode_init(logicvc);
|
|
if (ret) {
|
|
drm_err(drm_dev, "Failed to initialize KMS\n");
|
|
goto error_clocks;
|
|
}
|
|
|
|
ret = drm_dev_register(drm_dev, 0);
|
|
if (ret) {
|
|
drm_err(drm_dev, "Failed to register DRM device\n");
|
|
goto error_mode;
|
|
}
|
|
|
|
drm_fbdev_generic_setup(drm_dev, drm_dev->mode_config.preferred_depth);
|
|
|
|
return 0;
|
|
|
|
error_mode:
|
|
logicvc_mode_fini(logicvc);
|
|
|
|
error_clocks:
|
|
logicvc_clocks_unprepare(logicvc);
|
|
|
|
error_reserved_mem:
|
|
of_reserved_mem_device_release(dev);
|
|
|
|
error_early:
|
|
return ret;
|
|
}
|
|
|
|
static int logicvc_drm_remove(struct platform_device *pdev)
|
|
{
|
|
struct logicvc_drm *logicvc = platform_get_drvdata(pdev);
|
|
struct device *dev = &pdev->dev;
|
|
struct drm_device *drm_dev = &logicvc->drm_dev;
|
|
|
|
drm_dev_unregister(drm_dev);
|
|
drm_atomic_helper_shutdown(drm_dev);
|
|
|
|
logicvc_mode_fini(logicvc);
|
|
|
|
logicvc_clocks_unprepare(logicvc);
|
|
|
|
of_reserved_mem_device_release(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id logicvc_drm_of_table[] = {
|
|
{ .compatible = "xylon,logicvc-3.02.a-display" },
|
|
{ .compatible = "xylon,logicvc-4.01.a-display" },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, logicvc_drm_of_table);
|
|
|
|
static struct platform_driver logicvc_drm_platform_driver = {
|
|
.probe = logicvc_drm_probe,
|
|
.remove = logicvc_drm_remove,
|
|
.driver = {
|
|
.name = "logicvc-drm",
|
|
.of_match_table = logicvc_drm_of_table,
|
|
},
|
|
};
|
|
|
|
module_platform_driver(logicvc_drm_platform_driver);
|
|
|
|
MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
|
|
MODULE_DESCRIPTION("Xylon LogiCVC DRM driver");
|
|
MODULE_LICENSE("GPL");
|