10ff223d9SKory Maincent (TI.com) // SPDX-License-Identifier: GPL-2.0 20ff223d9SKory Maincent (TI.com) /* 30ff223d9SKory Maincent (TI.com) * Copyright (C) 2025 Bootlin 40ff223d9SKory Maincent (TI.com) * Author: Kory Maincent <kory.maincent@bootlin.com> 50ff223d9SKory Maincent (TI.com) * 60ff223d9SKory Maincent (TI.com) * To support the legacy "ti,tilcdc,panel" binding, the devicetree has to 70ff223d9SKory Maincent (TI.com) * be transformed to the new panel-dpi binding with the endpoint associated. 80ff223d9SKory Maincent (TI.com) */ 90ff223d9SKory Maincent (TI.com) 100ff223d9SKory Maincent (TI.com) #include <linux/kernel.h> 110ff223d9SKory Maincent (TI.com) #include <linux/of.h> 120ff223d9SKory Maincent (TI.com) #include <linux/of_fdt.h> 130ff223d9SKory Maincent (TI.com) #include <linux/slab.h> 140ff223d9SKory Maincent (TI.com) 150ff223d9SKory Maincent (TI.com) /* Embedded dtbo symbols created by cmd_wrap_S_dtb in scripts/Makefile.lib */ 160ff223d9SKory Maincent (TI.com) extern char __dtbo_tilcdc_panel_legacy_begin[]; 170ff223d9SKory Maincent (TI.com) extern char __dtbo_tilcdc_panel_legacy_end[]; 180ff223d9SKory Maincent (TI.com) 190ff223d9SKory Maincent (TI.com) static int __init 200ff223d9SKory Maincent (TI.com) tilcdc_panel_update_prop(struct of_changeset *ocs, struct device_node *node, 210ff223d9SKory Maincent (TI.com) char *name, void *val, int length) 220ff223d9SKory Maincent (TI.com) { 230ff223d9SKory Maincent (TI.com) struct property *prop; 240ff223d9SKory Maincent (TI.com) 250ff223d9SKory Maincent (TI.com) prop = kzalloc(sizeof(*prop), GFP_KERNEL); 260ff223d9SKory Maincent (TI.com) if (!prop) 270ff223d9SKory Maincent (TI.com) return -ENOMEM; 280ff223d9SKory Maincent (TI.com) 290ff223d9SKory Maincent (TI.com) prop->name = kstrdup(name, GFP_KERNEL); 300ff223d9SKory Maincent (TI.com) prop->length = length; 310ff223d9SKory Maincent (TI.com) prop->value = kmemdup(val, length, GFP_KERNEL); 320ff223d9SKory Maincent (TI.com) if (!prop->name || !prop->value) { 330ff223d9SKory Maincent (TI.com) kfree(prop->name); 340ff223d9SKory Maincent (TI.com) kfree(prop->value); 350ff223d9SKory Maincent (TI.com) kfree(prop); 360ff223d9SKory Maincent (TI.com) return -ENOMEM; 370ff223d9SKory Maincent (TI.com) } 380ff223d9SKory Maincent (TI.com) 390ff223d9SKory Maincent (TI.com) return of_changeset_update_property(ocs, node, prop); 400ff223d9SKory Maincent (TI.com) } 410ff223d9SKory Maincent (TI.com) 420ff223d9SKory Maincent (TI.com) static int __init tilcdc_panel_copy_props(struct device_node *old_panel, 430ff223d9SKory Maincent (TI.com) struct device_node *new_panel) 440ff223d9SKory Maincent (TI.com) { 450ff223d9SKory Maincent (TI.com) struct device_node *old_timing __free(device_node) = NULL; 460ff223d9SKory Maincent (TI.com) struct device_node *new_timing __free(device_node) = NULL; 470ff223d9SKory Maincent (TI.com) struct device_node *panel_info __free(device_node) = NULL; 480ff223d9SKory Maincent (TI.com) struct device_node *child __free(device_node) = NULL; 490ff223d9SKory Maincent (TI.com) u32 invert_pxl_clk = 0, sync_edge = 0; 500ff223d9SKory Maincent (TI.com) struct of_changeset ocs; 510ff223d9SKory Maincent (TI.com) struct property *prop; 520ff223d9SKory Maincent (TI.com) int ret; 530ff223d9SKory Maincent (TI.com) 540ff223d9SKory Maincent (TI.com) child = of_get_child_by_name(old_panel, "display-timings"); 550ff223d9SKory Maincent (TI.com) if (!child) 560ff223d9SKory Maincent (TI.com) return -EINVAL; 570ff223d9SKory Maincent (TI.com) 580ff223d9SKory Maincent (TI.com) /* The default display timing is the one specified as native-mode. 590ff223d9SKory Maincent (TI.com) * If no native-mode is specified then the first node is assumed 600ff223d9SKory Maincent (TI.com) * to be the native mode. 610ff223d9SKory Maincent (TI.com) */ 620ff223d9SKory Maincent (TI.com) old_timing = of_parse_phandle(child, "native-mode", 0); 630ff223d9SKory Maincent (TI.com) if (!old_timing) { 640ff223d9SKory Maincent (TI.com) old_timing = of_get_next_child(child, NULL); 650ff223d9SKory Maincent (TI.com) if (!old_timing) 660ff223d9SKory Maincent (TI.com) return -EINVAL; 670ff223d9SKory Maincent (TI.com) } 680ff223d9SKory Maincent (TI.com) 690ff223d9SKory Maincent (TI.com) panel_info = of_get_child_by_name(old_panel, "panel-info"); 700ff223d9SKory Maincent (TI.com) if (!panel_info) 710ff223d9SKory Maincent (TI.com) return -EINVAL; 720ff223d9SKory Maincent (TI.com) 730ff223d9SKory Maincent (TI.com) of_changeset_init(&ocs); 740ff223d9SKory Maincent (TI.com) 750ff223d9SKory Maincent (TI.com) /* Copy all panel properties to the new panel node */ 760ff223d9SKory Maincent (TI.com) for_each_property_of_node(old_panel, prop) { 770ff223d9SKory Maincent (TI.com) if (!strncmp(prop->name, "compatible", sizeof("compatible"))) 780ff223d9SKory Maincent (TI.com) continue; 790ff223d9SKory Maincent (TI.com) 800ff223d9SKory Maincent (TI.com) ret = tilcdc_panel_update_prop(&ocs, new_panel, prop->name, 810ff223d9SKory Maincent (TI.com) prop->value, prop->length); 820ff223d9SKory Maincent (TI.com) if (ret) 830ff223d9SKory Maincent (TI.com) goto destroy_ocs; 840ff223d9SKory Maincent (TI.com) } 850ff223d9SKory Maincent (TI.com) 860ff223d9SKory Maincent (TI.com) new_timing = of_changeset_create_node(&ocs, new_panel, "panel-timing"); 870ff223d9SKory Maincent (TI.com) if (!new_timing) { 880ff223d9SKory Maincent (TI.com) ret = -ENODEV; 890ff223d9SKory Maincent (TI.com) goto destroy_ocs; 900ff223d9SKory Maincent (TI.com) } 910ff223d9SKory Maincent (TI.com) 920ff223d9SKory Maincent (TI.com) /* Copy all panel timing properties to the new panel node */ 930ff223d9SKory Maincent (TI.com) for_each_property_of_node(old_timing, prop) { 940ff223d9SKory Maincent (TI.com) ret = tilcdc_panel_update_prop(&ocs, new_timing, prop->name, 950ff223d9SKory Maincent (TI.com) prop->value, prop->length); 960ff223d9SKory Maincent (TI.com) if (ret) 970ff223d9SKory Maincent (TI.com) goto destroy_ocs; 980ff223d9SKory Maincent (TI.com) } 990ff223d9SKory Maincent (TI.com) 1000ff223d9SKory Maincent (TI.com) /* Looked only for these two parameter as all the other are always 1010ff223d9SKory Maincent (TI.com) * set to default and not related to common DRM properties. 1020ff223d9SKory Maincent (TI.com) */ 1030ff223d9SKory Maincent (TI.com) of_property_read_u32(panel_info, "invert-pxl-clk", &invert_pxl_clk); 1040ff223d9SKory Maincent (TI.com) of_property_read_u32(panel_info, "sync-edge", &sync_edge); 1050ff223d9SKory Maincent (TI.com) 1060ff223d9SKory Maincent (TI.com) if (!invert_pxl_clk) { 1070ff223d9SKory Maincent (TI.com) ret = tilcdc_panel_update_prop(&ocs, new_timing, "pixelclk-active", 108*27a39e13SKory Maincent (TI) &(__be32){cpu_to_be32(1)}, sizeof(__be32)); 1090ff223d9SKory Maincent (TI.com) if (ret) 1100ff223d9SKory Maincent (TI.com) goto destroy_ocs; 1110ff223d9SKory Maincent (TI.com) } 1120ff223d9SKory Maincent (TI.com) 1130ff223d9SKory Maincent (TI.com) if (!sync_edge) { 1140ff223d9SKory Maincent (TI.com) ret = tilcdc_panel_update_prop(&ocs, new_timing, "syncclk-active", 115*27a39e13SKory Maincent (TI) &(__be32){cpu_to_be32(1)}, sizeof(__be32)); 1160ff223d9SKory Maincent (TI.com) if (ret) 1170ff223d9SKory Maincent (TI.com) goto destroy_ocs; 1180ff223d9SKory Maincent (TI.com) } 1190ff223d9SKory Maincent (TI.com) 1200ff223d9SKory Maincent (TI.com) /* Remove compatible property to avoid any driver compatible match */ 1210ff223d9SKory Maincent (TI.com) of_changeset_remove_property(&ocs, old_panel, 1220ff223d9SKory Maincent (TI.com) of_find_property(old_panel, "compatible", NULL)); 1230ff223d9SKory Maincent (TI.com) 1240ff223d9SKory Maincent (TI.com) of_changeset_apply(&ocs); 1250ff223d9SKory Maincent (TI.com) return 0; 1260ff223d9SKory Maincent (TI.com) 1270ff223d9SKory Maincent (TI.com) destroy_ocs: 1280ff223d9SKory Maincent (TI.com) of_changeset_destroy(&ocs); 1290ff223d9SKory Maincent (TI.com) return ret; 1300ff223d9SKory Maincent (TI.com) } 1310ff223d9SKory Maincent (TI.com) 1320ff223d9SKory Maincent (TI.com) static const struct of_device_id tilcdc_panel_of_match[] __initconst = { 1330ff223d9SKory Maincent (TI.com) { .compatible = "ti,tilcdc,panel", }, 1340ff223d9SKory Maincent (TI.com) {}, 1350ff223d9SKory Maincent (TI.com) }; 1360ff223d9SKory Maincent (TI.com) 1370ff223d9SKory Maincent (TI.com) static const struct of_device_id tilcdc_of_match[] __initconst = { 1380ff223d9SKory Maincent (TI.com) { .compatible = "ti,am33xx-tilcdc", }, 1390ff223d9SKory Maincent (TI.com) { .compatible = "ti,da850-tilcdc", }, 1400ff223d9SKory Maincent (TI.com) {}, 1410ff223d9SKory Maincent (TI.com) }; 1420ff223d9SKory Maincent (TI.com) 1430ff223d9SKory Maincent (TI.com) static int __init tilcdc_panel_legacy_init(void) 1440ff223d9SKory Maincent (TI.com) { 1450ff223d9SKory Maincent (TI.com) struct device_node *new_panel __free(device_node) = NULL; 1460ff223d9SKory Maincent (TI.com) struct device_node *panel __free(device_node) = NULL; 1470ff223d9SKory Maincent (TI.com) struct device_node *lcdc __free(device_node) = NULL; 1480ff223d9SKory Maincent (TI.com) void *dtbo_start; 1490ff223d9SKory Maincent (TI.com) u32 dtbo_size; 1500ff223d9SKory Maincent (TI.com) int ovcs_id; 1510ff223d9SKory Maincent (TI.com) int ret; 1520ff223d9SKory Maincent (TI.com) 1530ff223d9SKory Maincent (TI.com) lcdc = of_find_matching_node(NULL, tilcdc_of_match); 1540ff223d9SKory Maincent (TI.com) panel = of_find_matching_node(NULL, tilcdc_panel_of_match); 1550ff223d9SKory Maincent (TI.com) 1560ff223d9SKory Maincent (TI.com) if (!of_device_is_available(panel) || 1570ff223d9SKory Maincent (TI.com) !of_device_is_available(lcdc)) 1580ff223d9SKory Maincent (TI.com) return 0; 1590ff223d9SKory Maincent (TI.com) 1600ff223d9SKory Maincent (TI.com) dtbo_start = __dtbo_tilcdc_panel_legacy_begin; 1610ff223d9SKory Maincent (TI.com) dtbo_size = __dtbo_tilcdc_panel_legacy_end - 1620ff223d9SKory Maincent (TI.com) __dtbo_tilcdc_panel_legacy_begin; 1630ff223d9SKory Maincent (TI.com) 1640ff223d9SKory Maincent (TI.com) ret = of_overlay_fdt_apply(dtbo_start, dtbo_size, &ovcs_id, NULL); 1650ff223d9SKory Maincent (TI.com) if (ret) 1660ff223d9SKory Maincent (TI.com) return ret; 1670ff223d9SKory Maincent (TI.com) 1680ff223d9SKory Maincent (TI.com) new_panel = of_find_node_by_name(NULL, "tilcdc-panel-dpi"); 1690ff223d9SKory Maincent (TI.com) if (!new_panel) { 1700ff223d9SKory Maincent (TI.com) ret = -ENODEV; 1710ff223d9SKory Maincent (TI.com) goto overlay_remove; 1720ff223d9SKory Maincent (TI.com) } 1730ff223d9SKory Maincent (TI.com) 1740ff223d9SKory Maincent (TI.com) ret = tilcdc_panel_copy_props(panel, new_panel); 1750ff223d9SKory Maincent (TI.com) if (ret) 1760ff223d9SKory Maincent (TI.com) goto overlay_remove; 1770ff223d9SKory Maincent (TI.com) 1780ff223d9SKory Maincent (TI.com) return 0; 1790ff223d9SKory Maincent (TI.com) 1800ff223d9SKory Maincent (TI.com) overlay_remove: 1810ff223d9SKory Maincent (TI.com) of_overlay_remove(&ovcs_id); 1820ff223d9SKory Maincent (TI.com) return ret; 1830ff223d9SKory Maincent (TI.com) } 1840ff223d9SKory Maincent (TI.com) 1850ff223d9SKory Maincent (TI.com) subsys_initcall(tilcdc_panel_legacy_init); 186