Commit f298a2b9 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'du-next-20220303' of git://linuxtv.org/pinchartl/media into drm-next

- R-Car LVDS support for M3-W+ (R8A77961) SoC
- R-Car DU misc fixes and cleanups
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Link: https://patchwork.freedesktop.org/patch/msgid/YiCwy3FR3gPng4dN@pendragon.ideasonboard.com
parents 66a8af1f 8ba3c7bd
......@@ -28,6 +28,7 @@ properties:
- renesas,r8a7793-lvds # for R-Car M2-N compatible LVDS encoders
- renesas,r8a7795-lvds # for R-Car H3 compatible LVDS encoders
- renesas,r8a7796-lvds # for R-Car M3-W compatible LVDS encoders
- renesas,r8a77961-lvds # for R-Car M3-W+ compatible LVDS encoders
- renesas,r8a77965-lvds # for R-Car M3-N compatible LVDS encoders
- renesas,r8a77970-lvds # for R-Car V3M compatible LVDS encoders
- renesas,r8a77980-lvds # for R-Car V3H compatible LVDS encoders
......
......@@ -6,12 +6,6 @@ rcar-du-drm-y := rcar_du_crtc.o \
rcar_du_kms.o \
rcar_du_plane.o \
rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
rcar_du_of_lvds_r8a7790.dtb.o \
rcar_du_of_lvds_r8a7791.dtb.o \
rcar_du_of_lvds_r8a7793.dtb.o \
rcar_du_of_lvds_r8a7795.dtb.o \
rcar_du_of_lvds_r8a7796.dtb.o
rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o
......
......@@ -28,7 +28,6 @@
#include "rcar_du_drv.h"
#include "rcar_du_kms.h"
#include "rcar_du_of.h"
#include "rcar_du_regs.h"
/* -----------------------------------------------------------------------------
......@@ -634,6 +633,9 @@ static int rcar_du_probe(struct platform_device *pdev)
unsigned int mask;
int ret;
if (drm_firmware_drivers_only())
return -ENODEV;
/* Allocate and initialize the R-Car device structure. */
rcdu = devm_drm_dev_alloc(&pdev->dev, &rcar_du_driver,
struct rcar_du_device, ddev);
......@@ -699,22 +701,7 @@ static struct platform_driver rcar_du_platform_driver = {
},
};
static int __init rcar_du_init(void)
{
if (drm_firmware_drivers_only())
return -ENODEV;
rcar_du_of_init(rcar_du_of_table);
return platform_driver_register(&rcar_du_platform_driver);
}
module_init(rcar_du_init);
static void __exit rcar_du_exit(void)
{
platform_driver_unregister(&rcar_du_platform_driver);
}
module_exit(rcar_du_exit);
module_platform_driver(rcar_du_platform_driver);
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
......
// SPDX-License-Identifier: GPL-2.0
/*
* rcar_du_of.c - Legacy DT bindings compatibility
*
* Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*
* Based on work from Jyri Sarha <jsarha@ti.com>
* Copyright (C) 2015 Texas Instruments
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <linux/of_graph.h>
#include <linux/slab.h>
#include "rcar_du_crtc.h"
#include "rcar_du_drv.h"
#include "rcar_du_of.h"
/* -----------------------------------------------------------------------------
* Generic Overlay Handling
*/
struct rcar_du_of_overlay {
const char *compatible;
void *begin;
void *end;
};
#define RCAR_DU_OF_DTB(type, soc) \
extern char __dtb_rcar_du_of_##type##_##soc##_begin[]; \
extern char __dtb_rcar_du_of_##type##_##soc##_end[]
#define RCAR_DU_OF_OVERLAY(type, soc) \
{ \
.compatible = "renesas,du-" #soc, \
.begin = __dtb_rcar_du_of_##type##_##soc##_begin, \
.end = __dtb_rcar_du_of_##type##_##soc##_end, \
}
static int __init rcar_du_of_apply_overlay(const struct rcar_du_of_overlay *dtbs,
const char *compatible)
{
const struct rcar_du_of_overlay *dtb = NULL;
unsigned int i;
int ovcs_id;
for (i = 0; dtbs[i].compatible; ++i) {
if (!strcmp(dtbs[i].compatible, compatible)) {
dtb = &dtbs[i];
break;
}
}
if (!dtb)
return -ENODEV;
ovcs_id = 0;
return of_overlay_fdt_apply(dtb->begin, dtb->end - dtb->begin,
&ovcs_id);
}
static int __init rcar_du_of_add_property(struct of_changeset *ocs,
struct device_node *np,
const char *name, const void *value,
int length)
{
struct property *prop;
int ret = -ENOMEM;
prop = kzalloc(sizeof(*prop), GFP_KERNEL);
if (!prop)
return -ENOMEM;
prop->name = kstrdup(name, GFP_KERNEL);
if (!prop->name)
goto out_err;
prop->value = kmemdup(value, length, GFP_KERNEL);
if (!prop->value)
goto out_err;
of_property_set_flag(prop, OF_DYNAMIC);
prop->length = length;
ret = of_changeset_add_property(ocs, np, prop);
if (!ret)
return 0;
out_err:
kfree(prop->value);
kfree(prop->name);
kfree(prop);
return ret;
}
/* -----------------------------------------------------------------------------
* LVDS Overlays
*/
RCAR_DU_OF_DTB(lvds, r8a7790);
RCAR_DU_OF_DTB(lvds, r8a7791);
RCAR_DU_OF_DTB(lvds, r8a7793);
RCAR_DU_OF_DTB(lvds, r8a7795);
RCAR_DU_OF_DTB(lvds, r8a7796);
static const struct rcar_du_of_overlay rcar_du_lvds_overlays[] __initconst = {
RCAR_DU_OF_OVERLAY(lvds, r8a7790),
RCAR_DU_OF_OVERLAY(lvds, r8a7791),
RCAR_DU_OF_OVERLAY(lvds, r8a7793),
RCAR_DU_OF_OVERLAY(lvds, r8a7795),
RCAR_DU_OF_OVERLAY(lvds, r8a7796),
{ /* Sentinel */ },
};
static struct of_changeset rcar_du_lvds_changeset;
static void __init rcar_du_of_lvds_patch_one(struct device_node *lvds,
const struct of_phandle_args *clk,
struct device_node *local,
struct device_node *remote)
{
unsigned int psize;
unsigned int i;
__be32 value[4];
int ret;
/*
* Set the LVDS clocks property. This can't be performed by the overlay
* as the structure of the clock specifier has changed over time, and we
* don't know at compile time which binding version the system we will
* run on uses.
*/
if (clk->args_count >= ARRAY_SIZE(value) - 1)
return;
of_changeset_init(&rcar_du_lvds_changeset);
value[0] = cpu_to_be32(clk->np->phandle);
for (i = 0; i < clk->args_count; ++i)
value[i + 1] = cpu_to_be32(clk->args[i]);
psize = (clk->args_count + 1) * 4;
ret = rcar_du_of_add_property(&rcar_du_lvds_changeset, lvds,
"clocks", value, psize);
if (ret < 0)
goto done;
/*
* Insert the node in the OF graph: patch the LVDS ports remote-endpoint
* properties to point to the endpoints of the sibling nodes in the
* graph. This can't be performed by the overlay: on the input side the
* overlay would contain a phandle for the DU LVDS output port that
* would clash with the system DT, and on the output side the connection
* is board-specific.
*/
value[0] = cpu_to_be32(local->phandle);
value[1] = cpu_to_be32(remote->phandle);
for (i = 0; i < 2; ++i) {
struct device_node *endpoint;
endpoint = of_graph_get_endpoint_by_regs(lvds, i, 0);
if (!endpoint) {
ret = -EINVAL;
goto done;
}
ret = rcar_du_of_add_property(&rcar_du_lvds_changeset,
endpoint, "remote-endpoint",
&value[i], sizeof(value[i]));
of_node_put(endpoint);
if (ret < 0)
goto done;
}
ret = of_changeset_apply(&rcar_du_lvds_changeset);
done:
if (ret < 0)
of_changeset_destroy(&rcar_du_lvds_changeset);
}
struct lvds_of_data {
struct resource res;
struct of_phandle_args clkspec;
struct device_node *local;
struct device_node *remote;
};
static void __init rcar_du_of_lvds_patch(const struct of_device_id *of_ids)
{
const struct rcar_du_device_info *info;
const struct of_device_id *match;
struct lvds_of_data lvds_data[2] = { };
struct device_node *lvds_node;
struct device_node *soc_node;
struct device_node *du_node;
char compatible[22];
const char *soc_name;
unsigned int i;
int ret;
/* Get the DU node and exit if not present or disabled. */
du_node = of_find_matching_node_and_match(NULL, of_ids, &match);
if (!du_node || !of_device_is_available(du_node)) {
of_node_put(du_node);
return;
}
info = match->data;
soc_node = of_get_parent(du_node);
if (WARN_ON(info->num_lvds > ARRAY_SIZE(lvds_data)))
goto done;
/*
* Skip if the LVDS nodes already exists.
*
* The nodes are searched based on the compatible string, which we
* construct from the SoC name found in the DU compatible string. As a
* match has been found we know the compatible string matches the
* expected format and can thus skip some of the string manipulation
* normal safety checks.
*/
soc_name = strchr(match->compatible, '-') + 1;
sprintf(compatible, "renesas,%s-lvds", soc_name);
lvds_node = of_find_compatible_node(NULL, NULL, compatible);
if (lvds_node) {
of_node_put(lvds_node);
return;
}
/*
* Parse the DU node and store the register specifier, the clock
* specifier and the local and remote endpoint of the LVDS link for
* later use.
*/
for (i = 0; i < info->num_lvds; ++i) {
struct lvds_of_data *lvds = &lvds_data[i];
unsigned int port;
char name[7];
int index;
sprintf(name, "lvds.%u", i);
index = of_property_match_string(du_node, "clock-names", name);
if (index < 0)
continue;
ret = of_parse_phandle_with_args(du_node, "clocks",
"#clock-cells", index,
&lvds->clkspec);
if (ret < 0)
continue;
port = info->routes[RCAR_DU_OUTPUT_LVDS0 + i].port;
lvds->local = of_graph_get_endpoint_by_regs(du_node, port, 0);
if (!lvds->local)
continue;
lvds->remote = of_graph_get_remote_endpoint(lvds->local);
if (!lvds->remote)
continue;
index = of_property_match_string(du_node, "reg-names", name);
if (index < 0)
continue;
of_address_to_resource(du_node, index, &lvds->res);
}
/* Parse and apply the overlay. This will resolve phandles. */
ret = rcar_du_of_apply_overlay(rcar_du_lvds_overlays,
match->compatible);
if (ret < 0)
goto done;
/* Patch the newly created LVDS encoder nodes. */
for_each_child_of_node(soc_node, lvds_node) {
struct resource res;
if (!of_device_is_compatible(lvds_node, compatible))
continue;
/* Locate the lvds_data entry based on the resource start. */
ret = of_address_to_resource(lvds_node, 0, &res);
if (ret < 0)
continue;
for (i = 0; i < ARRAY_SIZE(lvds_data); ++i) {
if (lvds_data[i].res.start == res.start)
break;
}
if (i == ARRAY_SIZE(lvds_data))
continue;
/* Patch the LVDS encoder. */
rcar_du_of_lvds_patch_one(lvds_node, &lvds_data[i].clkspec,
lvds_data[i].local,
lvds_data[i].remote);
}
done:
for (i = 0; i < info->num_lvds; ++i) {
of_node_put(lvds_data[i].clkspec.np);
of_node_put(lvds_data[i].local);
of_node_put(lvds_data[i].remote);
}
of_node_put(soc_node);
of_node_put(du_node);
}
void __init rcar_du_of_init(const struct of_device_id *of_ids)
{
rcar_du_of_lvds_patch(of_ids);
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* rcar_du_of.h - Legacy DT bindings compatibility
*
* Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
#ifndef __RCAR_DU_OF_H__
#define __RCAR_DU_OF_H__
#include <linux/init.h>
struct of_device_id;
#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
void __init rcar_du_of_init(const struct of_device_id *of_ids);
#else
static inline void rcar_du_of_init(const struct of_device_id *of_ids) { }
#endif /* CONFIG_DRM_RCAR_LVDS */
#endif /* __RCAR_DU_OF_H__ */
// SPDX-License-Identifier: GPL-2.0
/*
* rcar_du_of_lvds_r8a7790.dts - Legacy LVDS DT bindings conversion for R8A7790
*
* Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
/dts-v1/;
/plugin/;
&{/} {
#address-cells = <2>;
#size-cells = <2>;
lvds@feb90000 {
compatible = "renesas,r8a7790-lvds";
reg = <0 0xfeb90000 0 0x1c>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lvds0_input: endpoint {
};
};
port@1 {
reg = <1>;
lvds0_out: endpoint {
};
};
};
};
lvds@feb94000 {
compatible = "renesas,r8a7790-lvds";
reg = <0 0xfeb94000 0 0x1c>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lvds1_input: endpoint {
};
};
port@1 {
reg = <1>;
lvds1_out: endpoint {
};
};
};
};
};
&{/display@feb00000/ports} {
port@1 {
endpoint {
remote-endpoint = <&lvds0_input>;
};
};
port@2 {
endpoint {
remote-endpoint = <&lvds1_input>;
};
};
};
// SPDX-License-Identifier: GPL-2.0
/*
* rcar_du_of_lvds_r8a7791.dts - Legacy LVDS DT bindings conversion for R8A7791
*
* Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
/dts-v1/;
/plugin/;
&{/} {
#address-cells = <2>;
#size-cells = <2>;
lvds@feb90000 {
compatible = "renesas,r8a7791-lvds";
reg = <0 0xfeb90000 0 0x1c>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lvds0_input: endpoint {
};
};
port@1 {
reg = <1>;
lvds0_out: endpoint {
};
};
};
};
};
&{/display@feb00000/ports} {
port@1 {
endpoint {
remote-endpoint = <&lvds0_input>;
};
};
};
// SPDX-License-Identifier: GPL-2.0
/*
* rcar_du_of_lvds_r8a7793.dts - Legacy LVDS DT bindings conversion for R8A7793
*
* Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
/dts-v1/;
/plugin/;
&{/} {
#address-cells = <2>;
#size-cells = <2>;
lvds@feb90000 {
compatible = "renesas,r8a7793-lvds";
reg = <0 0xfeb90000 0 0x1c>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lvds0_input: endpoint {
};
};
port@1 {
reg = <1>;
lvds0_out: endpoint {
};
};
};
};
};
&{/display@feb00000/ports} {
port@1 {
endpoint {
remote-endpoint = <&lvds0_input>;
};
};
};
// SPDX-License-Identifier: GPL-2.0
/*
* rcar_du_of_lvds_r8a7795.dts - Legacy LVDS DT bindings conversion for R8A7795
*
* Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
/dts-v1/;
/plugin/;
&{/soc} {
#address-cells = <2>;
#size-cells = <2>;
lvds@feb90000 {
compatible = "renesas,r8a7795-lvds";
reg = <0 0xfeb90000 0 0x14>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lvds0_input: endpoint {
};
};
port@1 {
reg = <1>;
lvds0_out: endpoint {
};
};
};
};
};
&{/soc/display@feb00000/ports} {
port@3 {
endpoint {
remote-endpoint = <&lvds0_input>;
};
};
};
// SPDX-License-Identifier: GPL-2.0
/*
* rcar_du_of_lvds_r8a7796.dts - Legacy LVDS DT bindings conversion for R8A7796
*
* Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
/dts-v1/;
/plugin/;
&{/soc} {
#address-cells = <2>;
#size-cells = <2>;
lvds@feb90000 {
compatible = "renesas,r8a7796-lvds";
reg = <0 0xfeb90000 0 0x14>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lvds0_input: endpoint {
};
};
port@1 {
reg = <1>;
lvds0_out: endpoint {
};
};
};
};
};
&{/soc/display@feb00000/ports} {
port@3 {
endpoint {
remote-endpoint = <&lvds0_input>;
};
};
};
......@@ -549,8 +549,10 @@ void __rcar_du_plane_setup(struct rcar_du_group *rgrp,
rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8,
state);
if (rcdu->info->gen < 3)
rcar_du_plane_setup_scanout(rgrp, state);
if (rcdu->info->gen >= 3)
return;
rcar_du_plane_setup_scanout(rgrp, state);
if (state->source == RCAR_DU_PLANE_VSPD1) {
unsigned int vspd1_sink = rgrp->index ? 2 : 0;
......@@ -558,6 +560,12 @@ void __rcar_du_plane_setup(struct rcar_du_group *rgrp,
if (rcdu->vspd1_sink != vspd1_sink) {
rcdu->vspd1_sink = vspd1_sink;
rcar_du_set_dpad0_vsp1_routing(rcdu);
/*
* Changes to the VSP1 sink take effect on DRES and thus
* need a restart of the group.
*/
rgrp->need_restart = true;
}
}
}
......
......@@ -84,15 +84,6 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
__rcar_du_plane_setup(crtc->group, &state);
/*
* Ensure that the plane source configuration takes effect by requesting
* a restart of the group. See rcar_du_plane_atomic_update() for a more
* detailed explanation.
*
* TODO: Check whether this is still needed on Gen3.
*/
crtc->group->need_restart = true;
vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
}
......
......@@ -901,6 +901,7 @@ static const struct of_device_id rcar_lvds_of_table[] = {
{ .compatible = "renesas,r8a7793-lvds", .data = &rcar_lvds_gen2_info },
{ .compatible = "renesas,r8a7795-lvds", .data = &rcar_lvds_gen3_info },
{ .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info },
{ .compatible = "renesas,r8a77961-lvds", .data = &rcar_lvds_gen3_info },
{ .compatible = "renesas,r8a77965-lvds", .data = &rcar_lvds_gen3_info },
{ .compatible = "renesas,r8a77970-lvds", .data = &rcar_lvds_r8a77970_info },
{ .compatible = "renesas,r8a77980-lvds", .data = &rcar_lvds_gen3_info },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment