root/drivers/gpu/drm/rcar-du/rcar_du_of.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. rcar_du_of_apply_overlay
  2. rcar_du_of_add_property
  3. rcar_du_of_lvds_patch_one
  4. rcar_du_of_lvds_patch
  5. rcar_du_of_init

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * rcar_du_of.c - Legacy DT bindings compatibility
   4  *
   5  * Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
   6  *
   7  * Based on work from Jyri Sarha <jsarha@ti.com>
   8  * Copyright (C) 2015 Texas Instruments
   9  */
  10 
  11 #include <linux/init.h>
  12 #include <linux/kernel.h>
  13 #include <linux/of.h>
  14 #include <linux/of_address.h>
  15 #include <linux/of_fdt.h>
  16 #include <linux/of_graph.h>
  17 #include <linux/slab.h>
  18 
  19 #include "rcar_du_crtc.h"
  20 #include "rcar_du_drv.h"
  21 #include "rcar_du_of.h"
  22 
  23 /* -----------------------------------------------------------------------------
  24  * Generic Overlay Handling
  25  */
  26 
  27 struct rcar_du_of_overlay {
  28         const char *compatible;
  29         void *begin;
  30         void *end;
  31 };
  32 
  33 #define RCAR_DU_OF_DTB(type, soc)                                       \
  34         extern char __dtb_rcar_du_of_##type##_##soc##_begin[];          \
  35         extern char __dtb_rcar_du_of_##type##_##soc##_end[]
  36 
  37 #define RCAR_DU_OF_OVERLAY(type, soc)                                   \
  38         {                                                               \
  39                 .compatible = "renesas,du-" #soc,                       \
  40                 .begin = __dtb_rcar_du_of_##type##_##soc##_begin,       \
  41                 .end = __dtb_rcar_du_of_##type##_##soc##_end,           \
  42         }
  43 
  44 static int __init rcar_du_of_apply_overlay(const struct rcar_du_of_overlay *dtbs,
  45                                            const char *compatible)
  46 {
  47         const struct rcar_du_of_overlay *dtb = NULL;
  48         unsigned int i;
  49         int ovcs_id;
  50 
  51         for (i = 0; dtbs[i].compatible; ++i) {
  52                 if (!strcmp(dtbs[i].compatible, compatible)) {
  53                         dtb = &dtbs[i];
  54                         break;
  55                 }
  56         }
  57 
  58         if (!dtb)
  59                 return -ENODEV;
  60 
  61         ovcs_id = 0;
  62         return of_overlay_fdt_apply(dtb->begin, dtb->end - dtb->begin,
  63                                     &ovcs_id);
  64 }
  65 
  66 static int __init rcar_du_of_add_property(struct of_changeset *ocs,
  67                                           struct device_node *np,
  68                                           const char *name, const void *value,
  69                                           int length)
  70 {
  71         struct property *prop;
  72         int ret = -ENOMEM;
  73 
  74         prop = kzalloc(sizeof(*prop), GFP_KERNEL);
  75         if (!prop)
  76                 return -ENOMEM;
  77 
  78         prop->name = kstrdup(name, GFP_KERNEL);
  79         if (!prop->name)
  80                 goto out_err;
  81 
  82         prop->value = kmemdup(value, length, GFP_KERNEL);
  83         if (!prop->value)
  84                 goto out_err;
  85 
  86         of_property_set_flag(prop, OF_DYNAMIC);
  87 
  88         prop->length = length;
  89 
  90         ret = of_changeset_add_property(ocs, np, prop);
  91         if (!ret)
  92                 return 0;
  93 
  94 out_err:
  95         kfree(prop->value);
  96         kfree(prop->name);
  97         kfree(prop);
  98         return ret;
  99 }
 100 
 101 /* -----------------------------------------------------------------------------
 102  * LVDS Overlays
 103  */
 104 
 105 RCAR_DU_OF_DTB(lvds, r8a7790);
 106 RCAR_DU_OF_DTB(lvds, r8a7791);
 107 RCAR_DU_OF_DTB(lvds, r8a7793);
 108 RCAR_DU_OF_DTB(lvds, r8a7795);
 109 RCAR_DU_OF_DTB(lvds, r8a7796);
 110 
 111 static const struct rcar_du_of_overlay rcar_du_lvds_overlays[] __initconst = {
 112         RCAR_DU_OF_OVERLAY(lvds, r8a7790),
 113         RCAR_DU_OF_OVERLAY(lvds, r8a7791),
 114         RCAR_DU_OF_OVERLAY(lvds, r8a7793),
 115         RCAR_DU_OF_OVERLAY(lvds, r8a7795),
 116         RCAR_DU_OF_OVERLAY(lvds, r8a7796),
 117         { /* Sentinel */ },
 118 };
 119 
 120 static struct of_changeset rcar_du_lvds_changeset;
 121 
 122 static void __init rcar_du_of_lvds_patch_one(struct device_node *lvds,
 123                                              const struct of_phandle_args *clk,
 124                                              struct device_node *local,
 125                                              struct device_node *remote)
 126 {
 127         unsigned int psize;
 128         unsigned int i;
 129         __be32 value[4];
 130         int ret;
 131 
 132         /*
 133          * Set the LVDS clocks property. This can't be performed by the overlay
 134          * as the structure of the clock specifier has changed over time, and we
 135          * don't know at compile time which binding version the system we will
 136          * run on uses.
 137          */
 138         if (clk->args_count >= ARRAY_SIZE(value) - 1)
 139                 return;
 140 
 141         of_changeset_init(&rcar_du_lvds_changeset);
 142 
 143         value[0] = cpu_to_be32(clk->np->phandle);
 144         for (i = 0; i < clk->args_count; ++i)
 145                 value[i + 1] = cpu_to_be32(clk->args[i]);
 146 
 147         psize = (clk->args_count + 1) * 4;
 148         ret = rcar_du_of_add_property(&rcar_du_lvds_changeset, lvds,
 149                                       "clocks", value, psize);
 150         if (ret < 0)
 151                 goto done;
 152 
 153         /*
 154          * Insert the node in the OF graph: patch the LVDS ports remote-endpoint
 155          * properties to point to the endpoints of the sibling nodes in the
 156          * graph. This can't be performed by the overlay: on the input side the
 157          * overlay would contain a phandle for the DU LVDS output port that
 158          * would clash with the system DT, and on the output side the connection
 159          * is board-specific.
 160          */
 161         value[0] = cpu_to_be32(local->phandle);
 162         value[1] = cpu_to_be32(remote->phandle);
 163 
 164         for (i = 0; i < 2; ++i) {
 165                 struct device_node *endpoint;
 166 
 167                 endpoint = of_graph_get_endpoint_by_regs(lvds, i, 0);
 168                 if (!endpoint) {
 169                         ret = -EINVAL;
 170                         goto done;
 171                 }
 172 
 173                 ret = rcar_du_of_add_property(&rcar_du_lvds_changeset,
 174                                               endpoint, "remote-endpoint",
 175                                               &value[i], sizeof(value[i]));
 176                 of_node_put(endpoint);
 177                 if (ret < 0)
 178                         goto done;
 179         }
 180 
 181         ret = of_changeset_apply(&rcar_du_lvds_changeset);
 182 
 183 done:
 184         if (ret < 0)
 185                 of_changeset_destroy(&rcar_du_lvds_changeset);
 186 }
 187 
 188 struct lvds_of_data {
 189         struct resource res;
 190         struct of_phandle_args clkspec;
 191         struct device_node *local;
 192         struct device_node *remote;
 193 };
 194 
 195 static void __init rcar_du_of_lvds_patch(const struct of_device_id *of_ids)
 196 {
 197         const struct rcar_du_device_info *info;
 198         const struct of_device_id *match;
 199         struct lvds_of_data lvds_data[2] = { };
 200         struct device_node *lvds_node;
 201         struct device_node *soc_node;
 202         struct device_node *du_node;
 203         char compatible[22];
 204         const char *soc_name;
 205         unsigned int i;
 206         int ret;
 207 
 208         /* Get the DU node and exit if not present or disabled. */
 209         du_node = of_find_matching_node_and_match(NULL, of_ids, &match);
 210         if (!du_node || !of_device_is_available(du_node)) {
 211                 of_node_put(du_node);
 212                 return;
 213         }
 214 
 215         info = match->data;
 216         soc_node = of_get_parent(du_node);
 217 
 218         if (WARN_ON(info->num_lvds > ARRAY_SIZE(lvds_data)))
 219                 goto done;
 220 
 221         /*
 222          * Skip if the LVDS nodes already exists.
 223          *
 224          * The nodes are searched based on the compatible string, which we
 225          * construct from the SoC name found in the DU compatible string. As a
 226          * match has been found we know the compatible string matches the
 227          * expected format and can thus skip some of the string manipulation
 228          * normal safety checks.
 229          */
 230         soc_name = strchr(match->compatible, '-') + 1;
 231         sprintf(compatible, "renesas,%s-lvds", soc_name);
 232         lvds_node = of_find_compatible_node(NULL, NULL, compatible);
 233         if (lvds_node) {
 234                 of_node_put(lvds_node);
 235                 return;
 236         }
 237 
 238         /*
 239          * Parse the DU node and store the register specifier, the clock
 240          * specifier and the local and remote endpoint of the LVDS link for
 241          * later use.
 242          */
 243         for (i = 0; i < info->num_lvds; ++i) {
 244                 struct lvds_of_data *lvds = &lvds_data[i];
 245                 unsigned int port;
 246                 char name[7];
 247                 int index;
 248 
 249                 sprintf(name, "lvds.%u", i);
 250                 index = of_property_match_string(du_node, "clock-names", name);
 251                 if (index < 0)
 252                         continue;
 253 
 254                 ret = of_parse_phandle_with_args(du_node, "clocks",
 255                                                  "#clock-cells", index,
 256                                                  &lvds->clkspec);
 257                 if (ret < 0)
 258                         continue;
 259 
 260                 port = info->routes[RCAR_DU_OUTPUT_LVDS0 + i].port;
 261 
 262                 lvds->local = of_graph_get_endpoint_by_regs(du_node, port, 0);
 263                 if (!lvds->local)
 264                         continue;
 265 
 266                 lvds->remote = of_graph_get_remote_endpoint(lvds->local);
 267                 if (!lvds->remote)
 268                         continue;
 269 
 270                 index = of_property_match_string(du_node, "reg-names", name);
 271                 if (index < 0)
 272                         continue;
 273 
 274                 of_address_to_resource(du_node, index, &lvds->res);
 275         }
 276 
 277         /* Parse and apply the overlay. This will resolve phandles. */
 278         ret = rcar_du_of_apply_overlay(rcar_du_lvds_overlays,
 279                                        match->compatible);
 280         if (ret < 0)
 281                 goto done;
 282 
 283         /* Patch the newly created LVDS encoder nodes. */
 284         for_each_child_of_node(soc_node, lvds_node) {
 285                 struct resource res;
 286 
 287                 if (!of_device_is_compatible(lvds_node, compatible))
 288                         continue;
 289 
 290                 /* Locate the lvds_data entry based on the resource start. */
 291                 ret = of_address_to_resource(lvds_node, 0, &res);
 292                 if (ret < 0)
 293                         continue;
 294 
 295                 for (i = 0; i < ARRAY_SIZE(lvds_data); ++i) {
 296                         if (lvds_data[i].res.start == res.start)
 297                                 break;
 298                 }
 299 
 300                 if (i == ARRAY_SIZE(lvds_data))
 301                         continue;
 302 
 303                 /* Patch the LVDS encoder. */
 304                 rcar_du_of_lvds_patch_one(lvds_node, &lvds_data[i].clkspec,
 305                                           lvds_data[i].local,
 306                                           lvds_data[i].remote);
 307         }
 308 
 309 done:
 310         for (i = 0; i < info->num_lvds; ++i) {
 311                 of_node_put(lvds_data[i].clkspec.np);
 312                 of_node_put(lvds_data[i].local);
 313                 of_node_put(lvds_data[i].remote);
 314         }
 315 
 316         of_node_put(soc_node);
 317         of_node_put(du_node);
 318 }
 319 
 320 void __init rcar_du_of_init(const struct of_device_id *of_ids)
 321 {
 322         rcar_du_of_lvds_patch(of_ids);
 323 }

/* [<][>][^][v][top][bottom][index][help] */