| // 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"); |
| } |