Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/tilcdc: Add DRM_TILCDC_SLAVE_COMPAT for ti,tilcdc,slave binding support

Adds a CONFIG_DRM_TILCDC_SLAVE_COMPAT module for "ti,tilcdc,slave"
node conversion. The implementation is in tilcdc_slave_compat.c and it
uses tilcdc_slave_compat.dts as a basis for creating a DTS
overlay. The DTS overlay adds an external tda998x encoder to tilcdc
that corresponds to the old tda998x based slave encoder.

Signed-off-by: Jyri Sarha <jsarha@ti.com>
Acked-by: Tomi Valkeinen <tomi.valkeinen@ti.com>

+382
+12
drivers/gpu/drm/tilcdc/Kconfig
··· 12 12 Choose this option if you have an TI SoC with LCDC display 13 13 controller, for example AM33xx in beagle-bone, DA8xx, or 14 14 OMAP-L1xx. This driver replaces the FB_DA8XX fbdev driver. 15 + 16 + config DRM_TILCDC_SLAVE_COMPAT 17 + bool "Support device tree blobs using TI LCDC Slave binding" 18 + depends on DRM_TILCDC 19 + default y 20 + select OF_RESOLVE 21 + select OF_OVERLAY 22 + help 23 + Choose this option if you need a kernel that is compatible 24 + with device tree blobs using the obsolete "ti,tilcdc,slave" 25 + binding. If you find "ti,tilcdc,slave"-string from your DTB, 26 + you probably need this. Otherwise you do not.
+3
drivers/gpu/drm/tilcdc/Makefile
··· 3 3 ccflags-y += -Werror 4 4 endif 5 5 6 + obj-$(CONFIG_DRM_TILCDC_SLAVE_COMPAT) += tilcdc_slave_compat.o \ 7 + tilcdc_slave_compat.dtb.o 8 + 6 9 tilcdc-y := \ 7 10 tilcdc_crtc.o \ 8 11 tilcdc_tfp410.o \
+270
drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c
··· 1 + /* 2 + * Copyright (C) 2015 Texas Instruments 3 + * Author: Jyri Sarha <jsarha@ti.com> 4 + * 5 + * This program is free software; you can redistribute it and/or modify it 6 + * under the terms of the GNU General Public License version 2 as published by 7 + * the Free Software Foundation. 8 + * 9 + */ 10 + 11 + /* 12 + * To support the old "ti,tilcdc,slave" binding the binding has to be 13 + * transformed to the new external encoder binding. 14 + */ 15 + 16 + #include <linux/kernel.h> 17 + #include <linux/of.h> 18 + #include <linux/of_graph.h> 19 + #include <linux/of_fdt.h> 20 + #include <linux/slab.h> 21 + #include <linux/list.h> 22 + 23 + #include "tilcdc_slave_compat.h" 24 + 25 + struct kfree_table { 26 + int total; 27 + int num; 28 + void **table; 29 + }; 30 + 31 + static int __init kfree_table_init(struct kfree_table *kft) 32 + { 33 + kft->total = 32; 34 + kft->num = 0; 35 + kft->table = kmalloc(kft->total * sizeof(*kft->table), 36 + GFP_KERNEL); 37 + if (!kft->table) 38 + return -ENOMEM; 39 + 40 + return 0; 41 + } 42 + 43 + static int __init kfree_table_add(struct kfree_table *kft, void *p) 44 + { 45 + if (kft->num == kft->total) { 46 + void **old = kft->table; 47 + 48 + kft->total *= 2; 49 + kft->table = krealloc(old, kft->total * sizeof(*kft->table), 50 + GFP_KERNEL); 51 + if (!kft->table) { 52 + kft->table = old; 53 + kfree(p); 54 + return -ENOMEM; 55 + } 56 + } 57 + kft->table[kft->num++] = p; 58 + return 0; 59 + } 60 + 61 + static void __init kfree_table_free(struct kfree_table *kft) 62 + { 63 + int i; 64 + 65 + for (i = 0; i < kft->num; i++) 66 + kfree(kft->table[i]); 67 + 68 + kfree(kft->table); 69 + } 70 + 71 + static 72 + struct property * __init tilcdc_prop_dup(const struct property *prop, 73 + struct kfree_table *kft) 74 + { 75 + struct property *nprop; 76 + 77 + nprop = kzalloc(sizeof(*nprop), GFP_KERNEL); 78 + if (!nprop || kfree_table_add(kft, nprop)) 79 + return NULL; 80 + 81 + nprop->name = kstrdup(prop->name, GFP_KERNEL); 82 + if (!nprop->name || kfree_table_add(kft, nprop->name)) 83 + return NULL; 84 + 85 + nprop->value = kmemdup(prop->value, prop->length, GFP_KERNEL); 86 + if (!nprop->value || kfree_table_add(kft, nprop->value)) 87 + return NULL; 88 + 89 + nprop->length = prop->length; 90 + 91 + return nprop; 92 + } 93 + 94 + static void __init tilcdc_copy_props(struct device_node *from, 95 + struct device_node *to, 96 + const char * const props[], 97 + struct kfree_table *kft) 98 + { 99 + struct property *prop; 100 + int i; 101 + 102 + for (i = 0; props[i]; i++) { 103 + prop = of_find_property(from, props[i], NULL); 104 + if (!prop) 105 + continue; 106 + 107 + prop = tilcdc_prop_dup(prop, kft); 108 + if (!prop) 109 + continue; 110 + 111 + prop->next = to->properties; 112 + to->properties = prop; 113 + } 114 + } 115 + 116 + static int __init tilcdc_prop_str_update(struct property *prop, 117 + const char *str, 118 + struct kfree_table *kft) 119 + { 120 + prop->value = kstrdup(str, GFP_KERNEL); 121 + if (kfree_table_add(kft, prop->value) || !prop->value) 122 + return -ENOMEM; 123 + prop->length = strlen(str)+1; 124 + return 0; 125 + } 126 + 127 + static void __init tilcdc_node_disable(struct device_node *node) 128 + { 129 + struct property *prop; 130 + 131 + prop = kzalloc(sizeof(*prop), GFP_KERNEL); 132 + if (!prop) 133 + return; 134 + 135 + prop->name = "status"; 136 + prop->value = "disabled"; 137 + prop->length = strlen((char *)prop->value)+1; 138 + 139 + of_update_property(node, prop); 140 + } 141 + 142 + struct device_node * __init tilcdc_get_overlay(struct kfree_table *kft) 143 + { 144 + const int size = __dtb_tilcdc_slave_compat_end - 145 + __dtb_tilcdc_slave_compat_begin; 146 + static void *overlay_data; 147 + struct device_node *overlay; 148 + int ret; 149 + 150 + if (!size) { 151 + pr_warn("%s: No overlay data\n", __func__); 152 + return NULL; 153 + } 154 + 155 + overlay_data = kmemdup(__dtb_tilcdc_slave_compat_begin, 156 + size, GFP_KERNEL); 157 + if (!overlay_data || kfree_table_add(kft, overlay_data)) 158 + return NULL; 159 + 160 + of_fdt_unflatten_tree(overlay_data, &overlay); 161 + if (!overlay) { 162 + pr_warn("%s: Unfattening overlay tree failed\n", __func__); 163 + return NULL; 164 + } 165 + 166 + of_node_set_flag(overlay, OF_DETACHED); 167 + ret = of_resolve_phandles(overlay); 168 + if (ret) { 169 + pr_err("%s: Failed to resolve phandles: %d\n", __func__, ret); 170 + return NULL; 171 + } 172 + 173 + return overlay; 174 + } 175 + 176 + static const struct of_device_id tilcdc_slave_of_match[] __initconst = { 177 + { .compatible = "ti,tilcdc,slave", }, 178 + {}, 179 + }; 180 + 181 + static const struct of_device_id tilcdc_of_match[] __initconst = { 182 + { .compatible = "ti,am33xx-tilcdc", }, 183 + {}, 184 + }; 185 + 186 + static const struct of_device_id tilcdc_tda998x_of_match[] __initconst = { 187 + { .compatible = "nxp,tda998x", }, 188 + {}, 189 + }; 190 + 191 + static const char * const tilcdc_slave_props[] __initconst = { 192 + "pinctrl-names", 193 + "pinctrl-0", 194 + "pinctrl-1", 195 + NULL 196 + }; 197 + 198 + void __init tilcdc_convert_slave_node(void) 199 + { 200 + struct device_node *slave = NULL, *lcdc = NULL; 201 + struct device_node *i2c = NULL, *fragment = NULL; 202 + struct device_node *overlay, *encoder; 203 + struct property *prop; 204 + /* For all memory needed for the overlay tree. This memory can 205 + be freed after the overlay has been applied. */ 206 + struct kfree_table kft; 207 + int ret; 208 + 209 + if (kfree_table_init(&kft)) 210 + goto out; 211 + 212 + lcdc = of_find_matching_node(NULL, tilcdc_of_match); 213 + slave = of_find_matching_node(NULL, tilcdc_slave_of_match); 214 + 215 + if (!slave || !of_device_is_available(lcdc)) 216 + goto out; 217 + 218 + i2c = of_parse_phandle(slave, "i2c", 0); 219 + if (!i2c) { 220 + pr_err("%s: Can't find i2c node trough phandle\n", __func__); 221 + goto out; 222 + } 223 + 224 + overlay = tilcdc_get_overlay(&kft); 225 + if (!overlay) 226 + goto out; 227 + 228 + encoder = of_find_matching_node(overlay, tilcdc_tda998x_of_match); 229 + if (!encoder) { 230 + pr_err("%s: Failed to find tda998x node\n", __func__); 231 + goto out; 232 + } 233 + 234 + tilcdc_copy_props(slave, encoder, tilcdc_slave_props, &kft); 235 + 236 + for_each_child_of_node(overlay, fragment) { 237 + prop = of_find_property(fragment, "target-path", NULL); 238 + if (!prop) 239 + continue; 240 + if (!strncmp("i2c", (char *)prop->value, prop->length)) 241 + if (tilcdc_prop_str_update(prop, i2c->full_name, &kft)) 242 + goto out; 243 + if (!strncmp("lcdc", (char *)prop->value, prop->length)) 244 + if (tilcdc_prop_str_update(prop, lcdc->full_name, &kft)) 245 + goto out; 246 + } 247 + 248 + tilcdc_node_disable(slave); 249 + 250 + ret = of_overlay_create(overlay); 251 + if (ret) 252 + pr_err("%s: Creating overlay failed: %d\n", __func__, ret); 253 + else 254 + pr_info("%s: ti,tilcdc,slave node successfully converted\n", 255 + __func__); 256 + out: 257 + kfree_table_free(&kft); 258 + of_node_put(i2c); 259 + of_node_put(slave); 260 + of_node_put(lcdc); 261 + of_node_put(fragment); 262 + } 263 + 264 + int __init tilcdc_slave_compat_init(void) 265 + { 266 + tilcdc_convert_slave_node(); 267 + return 0; 268 + } 269 + 270 + subsys_initcall(tilcdc_slave_compat_init);
+72
drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dts
··· 1 + /* 2 + * DTS overlay for converting ti,tilcdc,slave binding to new binding. 3 + * 4 + * Copyright (C) 2015 Texas Instruments Inc. 5 + * Author: Jyri Sarha <jsarha@ti.com> 6 + * 7 + * This program is free software; you can redistribute it and/or 8 + * modify it under the terms of the GNU General Public License 9 + * version 2 as published by the Free Software Foundation. 10 + */ 11 + 12 + /* 13 + * target-path property values are simple tags that are replaced with 14 + * correct values in tildcdc_slave_compat.c. Some properties are also 15 + * copied over from the ti,tilcdc,slave node. 16 + */ 17 + 18 + /dts-v1/; 19 + / { 20 + fragment@0 { 21 + target-path = "i2c"; 22 + __overlay__ { 23 + #address-cells = <1>; 24 + #size-cells = <0>; 25 + tda19988 { 26 + compatible = "nxp,tda998x"; 27 + reg = <0x70>; 28 + status = "okay"; 29 + 30 + port { 31 + hdmi_0: endpoint@0 { 32 + remote-endpoint = <&lcd_0>; 33 + }; 34 + }; 35 + }; 36 + }; 37 + }; 38 + 39 + fragment@1 { 40 + target-path = "lcdc"; 41 + __overlay__ { 42 + port { 43 + lcd_0: endpoint@0 { 44 + remote-endpoint = <&hdmi_0>; 45 + }; 46 + }; 47 + }; 48 + }; 49 + 50 + __local_fixups__ { 51 + fragment@0 { 52 + __overlay__ { 53 + tda19988 { 54 + port { 55 + endpoint@0 { 56 + remote-endpoint = <0>; 57 + }; 58 + }; 59 + }; 60 + }; 61 + }; 62 + fragment@1 { 63 + __overlay__ { 64 + port { 65 + endpoint@0 { 66 + remote-endpoint = <0>; 67 + }; 68 + }; 69 + }; 70 + }; 71 + }; 72 + };
+25
drivers/gpu/drm/tilcdc/tilcdc_slave_compat.h
··· 1 + /* 2 + * Copyright (C) 2015 Texas Instruments 3 + * Author: Jyri Sarha <jsarha@ti.com> 4 + * 5 + * This program is free software; you can redistribute it and/or modify it 6 + * under the terms of the GNU General Public License version 2 as published by 7 + * the Free Software Foundation. 8 + * 9 + * This program is distributed in the hope that it will be useful, but WITHOUT 10 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 + * more details. 13 + * 14 + * You should have received a copy of the GNU General Public License along with 15 + * this program. If not, see <http://www.gnu.org/licenses/>. 16 + */ 17 + /* This header declares the symbols defined in tilcdc_slave_compat.dts */ 18 + 19 + #ifndef __TILCDC_SLAVE_COMPAT_H__ 20 + #define __TILCDC_SLAVE_COMPAT_H__ 21 + 22 + extern uint8_t __dtb_tilcdc_slave_compat_begin[]; 23 + extern uint8_t __dtb_tilcdc_slave_compat_end[]; 24 + 25 + #endif /* __TILCDC_SLAVE_COMPAT_H__ */