root/drivers/gpu/drm/logicvc/logicvc_of.c
// 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");
}