1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2019-2022 Bootlin
4  * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
5  */
6 
7 #include <drm/drm_print.h>
8 
9 #include "logicvc_drm.h"
10 #include "logicvc_layer.h"
11 #include "logicvc_of.h"
12 
13 static struct logicvc_of_property_sv logicvc_of_display_interface_sv[] = {
14 	{ "lvds-4bits",	LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS },
15 	{ "lvds-3bits",	LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS },
16 	{ },
17 };
18 
19 static struct logicvc_of_property_sv logicvc_of_display_colorspace_sv[] = {
20 	{ "rgb",	LOGICVC_DISPLAY_COLORSPACE_RGB },
21 	{ "yuv422",	LOGICVC_DISPLAY_COLORSPACE_YUV422 },
22 	{ "yuv444",	LOGICVC_DISPLAY_COLORSPACE_YUV444 },
23 	{ },
24 };
25 
26 static struct logicvc_of_property_sv logicvc_of_layer_colorspace_sv[] = {
27 	{ "rgb",	LOGICVC_LAYER_COLORSPACE_RGB },
28 	{ "yuv",	LOGICVC_LAYER_COLORSPACE_YUV },
29 	{ },
30 };
31 
32 static struct logicvc_of_property_sv logicvc_of_layer_alpha_mode_sv[] = {
33 	{ "layer",	LOGICVC_LAYER_ALPHA_LAYER },
34 	{ "pixel",	LOGICVC_LAYER_ALPHA_PIXEL },
35 	{ },
36 };
37 
38 static struct logicvc_of_property logicvc_of_properties[] = {
39 	[LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE] = {
40 		.name		= "xylon,display-interface",
41 		.sv		= logicvc_of_display_interface_sv,
42 		.range		= {
43 			LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS,
44 			LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS,
45 		},
46 	},
47 	[LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE] = {
48 		.name		= "xylon,display-colorspace",
49 		.sv		= logicvc_of_display_colorspace_sv,
50 		.range		= {
51 			LOGICVC_DISPLAY_COLORSPACE_RGB,
52 			LOGICVC_DISPLAY_COLORSPACE_YUV444,
53 		},
54 	},
55 	[LOGICVC_OF_PROPERTY_DISPLAY_DEPTH] = {
56 		.name		= "xylon,display-depth",
57 		.range		= { 8, 24 },
58 	},
59 	[LOGICVC_OF_PROPERTY_ROW_STRIDE] = {
60 		.name		= "xylon,row-stride",
61 	},
62 	[LOGICVC_OF_PROPERTY_DITHERING] = {
63 		.name		= "xylon,dithering",
64 		.optional	= true,
65 	},
66 	[LOGICVC_OF_PROPERTY_BACKGROUND_LAYER] = {
67 		.name		= "xylon,background-layer",
68 		.optional	= true,
69 	},
70 	[LOGICVC_OF_PROPERTY_LAYERS_CONFIGURABLE] = {
71 		.name		= "xylon,layers-configurable",
72 		.optional	= true,
73 	},
74 	[LOGICVC_OF_PROPERTY_LAYERS_COUNT] = {
75 		.name		= "xylon,layers-count",
76 	},
77 	[LOGICVC_OF_PROPERTY_LAYER_DEPTH] = {
78 		.name		= "xylon,layer-depth",
79 		.range		= { 8, 24 },
80 	},
81 	[LOGICVC_OF_PROPERTY_LAYER_COLORSPACE] = {
82 		.name		= "xylon,layer-colorspace",
83 		.sv		= logicvc_of_layer_colorspace_sv,
84 		.range		= {
85 			LOGICVC_LAYER_COLORSPACE_RGB,
86 			LOGICVC_LAYER_COLORSPACE_RGB,
87 		},
88 	},
89 	[LOGICVC_OF_PROPERTY_LAYER_ALPHA_MODE] = {
90 		.name		= "xylon,layer-alpha-mode",
91 		.sv		= logicvc_of_layer_alpha_mode_sv,
92 		.range		= {
93 			LOGICVC_LAYER_ALPHA_LAYER,
94 			LOGICVC_LAYER_ALPHA_PIXEL,
95 		},
96 	},
97 	[LOGICVC_OF_PROPERTY_LAYER_BASE_OFFSET] = {
98 		.name		= "xylon,layer-base-offset",
99 	},
100 	[LOGICVC_OF_PROPERTY_LAYER_BUFFER_OFFSET] = {
101 		.name		= "xylon,layer-buffer-offset",
102 	},
103 	[LOGICVC_OF_PROPERTY_LAYER_PRIMARY] = {
104 		.name		= "xylon,layer-primary",
105 		.optional	= true,
106 	},
107 };
108 
logicvc_of_property_sv_value(struct logicvc_of_property_sv * sv,const char * string,u32 * value)109 static int logicvc_of_property_sv_value(struct logicvc_of_property_sv *sv,
110 					const char *string, u32 *value)
111 {
112 	unsigned int i = 0;
113 
114 	while (sv[i].string) {
115 		if (!strcmp(sv[i].string, string)) {
116 			*value = sv[i].value;
117 			return 0;
118 		}
119 
120 		i++;
121 	}
122 
123 	return -EINVAL;
124 }
125 
logicvc_of_property_parse_u32(struct device_node * of_node,unsigned int index,u32 * target)126 int logicvc_of_property_parse_u32(struct device_node *of_node,
127 				  unsigned int index, u32 *target)
128 {
129 	struct logicvc_of_property *property;
130 	const char *string;
131 	u32 value;
132 	int ret;
133 
134 	if (index >= LOGICVC_OF_PROPERTY_MAXIMUM)
135 		return -EINVAL;
136 
137 	property = &logicvc_of_properties[index];
138 
139 	if (!property->optional &&
140 	    !of_property_read_bool(of_node, property->name))
141 		return -ENODEV;
142 
143 	if (property->sv) {
144 		ret = of_property_read_string(of_node, property->name, &string);
145 		if (ret)
146 			return ret;
147 
148 		ret = logicvc_of_property_sv_value(property->sv, string,
149 						   &value);
150 		if (ret)
151 			return ret;
152 	} else {
153 		ret = of_property_read_u32(of_node, property->name, &value);
154 		if (ret)
155 			return ret;
156 	}
157 
158 	if (property->range[0] || property->range[1])
159 		if (value < property->range[0] || value > property->range[1])
160 			return -ERANGE;
161 
162 	*target = value;
163 
164 	return 0;
165 }
166 
logicvc_of_property_parse_bool(struct device_node * of_node,unsigned int index,bool * target)167 void logicvc_of_property_parse_bool(struct device_node *of_node,
168 				    unsigned int index, bool *target)
169 {
170 	struct logicvc_of_property *property;
171 
172 	if (index >= LOGICVC_OF_PROPERTY_MAXIMUM) {
173 		/* Fallback. */
174 		*target = false;
175 		return;
176 	}
177 
178 	property = &logicvc_of_properties[index];
179 	*target = of_property_read_bool(of_node, property->name);
180 }
181 
logicvc_of_node_is_layer(struct device_node * of_node)182 bool logicvc_of_node_is_layer(struct device_node *of_node)
183 {
184 	return !of_node_cmp(of_node->name, "layer");
185 }
186