| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * Copyright (C) 2019-2022 Bootlin | |
| * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> | |
| */ | |
| #include <drm/drm_print.h> | |
| #include "logicvc_drm.h" | |
| #include "logicvc_layer.h" | |
| #include "logicvc_of.h" | |
| static struct logicvc_of_property_sv logicvc_of_display_interface_sv[] = { | |
| { "lvds-4bits", LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS }, | |
| { "lvds-3bits", LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS }, | |
| { }, | |
| }; | |
| static struct logicvc_of_property_sv logicvc_of_display_colorspace_sv[] = { | |
| { "rgb", LOGICVC_DISPLAY_COLORSPACE_RGB }, | |
| { "yuv422", LOGICVC_DISPLAY_COLORSPACE_YUV422 }, | |
| { "yuv444", LOGICVC_DISPLAY_COLORSPACE_YUV444 }, | |
| { }, | |
| }; | |
| static struct logicvc_of_property_sv logicvc_of_layer_colorspace_sv[] = { | |
| { "rgb", LOGICVC_LAYER_COLORSPACE_RGB }, | |
| { "yuv", LOGICVC_LAYER_COLORSPACE_YUV }, | |
| { }, | |
| }; | |
| static struct logicvc_of_property_sv logicvc_of_layer_alpha_mode_sv[] = { | |
| { "layer", LOGICVC_LAYER_ALPHA_LAYER }, | |
| { "pixel", LOGICVC_LAYER_ALPHA_PIXEL }, | |
| { }, | |
| }; | |
| static struct logicvc_of_property logicvc_of_properties[] = { | |
| [LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE] = { | |
| .name = "xylon,display-interface", | |
| .sv = logicvc_of_display_interface_sv, | |
| .range = { | |
| LOGICVC_DISPLAY_INTERFACE_LVDS_4BITS, | |
| LOGICVC_DISPLAY_INTERFACE_LVDS_3BITS, | |
| }, | |
| }, | |
| [LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE] = { | |
| .name = "xylon,display-colorspace", | |
| .sv = logicvc_of_display_colorspace_sv, | |
| .range = { | |
| LOGICVC_DISPLAY_COLORSPACE_RGB, | |
| LOGICVC_DISPLAY_COLORSPACE_YUV444, | |
| }, | |
| }, | |
| [LOGICVC_OF_PROPERTY_DISPLAY_DEPTH] = { | |
| .name = "xylon,display-depth", | |
| .range = { 8, 24 }, | |
| }, | |
| [LOGICVC_OF_PROPERTY_ROW_STRIDE] = { | |
| .name = "xylon,row-stride", | |
| }, | |
| [LOGICVC_OF_PROPERTY_DITHERING] = { | |
| .name = "xylon,dithering", | |
| .optional = true, | |
| }, | |
| [LOGICVC_OF_PROPERTY_BACKGROUND_LAYER] = { | |
| .name = "xylon,background-layer", | |
| .optional = true, | |
| }, | |
| [LOGICVC_OF_PROPERTY_LAYERS_CONFIGURABLE] = { | |
| .name = "xylon,layers-configurable", | |
| .optional = true, | |
| }, | |
| [LOGICVC_OF_PROPERTY_LAYERS_COUNT] = { | |
| .name = "xylon,layers-count", | |
| }, | |
| [LOGICVC_OF_PROPERTY_LAYER_DEPTH] = { | |
| .name = "xylon,layer-depth", | |
| .range = { 8, 24 }, | |
| }, | |
| [LOGICVC_OF_PROPERTY_LAYER_COLORSPACE] = { | |
| .name = "xylon,layer-colorspace", | |
| .sv = logicvc_of_layer_colorspace_sv, | |
| .range = { | |
| LOGICVC_LAYER_COLORSPACE_RGB, | |
| LOGICVC_LAYER_COLORSPACE_RGB, | |
| }, | |
| }, | |
| [LOGICVC_OF_PROPERTY_LAYER_ALPHA_MODE] = { | |
| .name = "xylon,layer-alpha-mode", | |
| .sv = logicvc_of_layer_alpha_mode_sv, | |
| .range = { | |
| LOGICVC_LAYER_ALPHA_LAYER, | |
| LOGICVC_LAYER_ALPHA_PIXEL, | |
| }, | |
| }, | |
| [LOGICVC_OF_PROPERTY_LAYER_BASE_OFFSET] = { | |
| .name = "xylon,layer-base-offset", | |
| }, | |
| [LOGICVC_OF_PROPERTY_LAYER_BUFFER_OFFSET] = { | |
| .name = "xylon,layer-buffer-offset", | |
| }, | |
| [LOGICVC_OF_PROPERTY_LAYER_PRIMARY] = { | |
| .name = "xylon,layer-primary", | |
| .optional = true, | |
| }, | |
| }; | |
| static int logicvc_of_property_sv_value(struct logicvc_of_property_sv *sv, | |
| const char *string, u32 *value) | |
| { | |
| unsigned int i = 0; | |
| while (sv[i].string) { | |
| if (!strcmp(sv[i].string, string)) { | |
| *value = sv[i].value; | |
| return 0; | |
| } | |
| i++; | |
| } | |
| return -EINVAL; | |
| } | |
| int logicvc_of_property_parse_u32(struct device_node *of_node, | |
| unsigned int index, u32 *target) | |
| { | |
| struct logicvc_of_property *property; | |
| const char *string; | |
| u32 value; | |
| int ret; | |
| if (index >= LOGICVC_OF_PROPERTY_MAXIMUM) | |
| return -EINVAL; | |
| property = &logicvc_of_properties[index]; | |
| if (!property->optional && | |
| !of_property_read_bool(of_node, property->name)) | |
| return -ENODEV; | |
| if (property->sv) { | |
| ret = of_property_read_string(of_node, property->name, &string); | |
| if (ret) | |
| return ret; | |
| ret = logicvc_of_property_sv_value(property->sv, string, | |
| &value); | |
| if (ret) | |
| return ret; | |
| } else { | |
| ret = of_property_read_u32(of_node, property->name, &value); | |
| if (ret) | |
| return ret; | |
| } | |
| if (property->range[0] || property->range[1]) | |
| if (value < property->range[0] || value > property->range[1]) | |
| return -ERANGE; | |
| *target = value; | |
| return 0; | |
| } | |
| void logicvc_of_property_parse_bool(struct device_node *of_node, | |
| unsigned int index, bool *target) | |
| { | |
| struct logicvc_of_property *property; | |
| if (index >= LOGICVC_OF_PROPERTY_MAXIMUM) { | |
| /* Fallback. */ | |
| *target = false; | |
| return; | |
| } | |
| property = &logicvc_of_properties[index]; | |
| *target = of_property_read_bool(of_node, property->name); | |
| } | |
| bool logicvc_of_node_is_layer(struct device_node *of_node) | |
| { | |
| return !of_node_cmp(of_node->name, "layer"); | |
| } |