xref: /linux/drivers/gpu/drm/tilcdc/tilcdc_panel_legacy.c (revision 4a57e0913e8c7fff407e97909f4ae48caa84d612)
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