[PATCH 11/11] ARM: versatile: move CLCD configuration to device tree

Linus Walleij linus.walleij at linaro.org
Sun Feb 21 14:39:24 PST 2016


On Thu, Feb 18, 2016 at 12:52 PM, Tomi Valkeinen <tomi.valkeinen at ti.com> wrote:

> For panels we need DT fragments. The question is where these fragments
> are and, possibly, who who loads them.

I hacked something up that augments the device tree from the kernel,
given you have a node with all the props you want to augment, tell me
what you think of this and whether I should continue in this direction...
also the DT people need to be involved:

#include "../../drivers/of/of_private.h"
/* Obviously make the property duplication function public instead,
it's a test */

struct versatile_panel {
    u32 id;
    char *compatible;
    u32 clock_frequency;
    u32 pixelclk_active;
    u32 hsync_active;
    u32 vsync_active;
    u32 de_active;
    u32 hactive;
    u32 hback_porch;
    u32 hfront_porch;
    u32 hsync_len;
    u32 vactive;
    u32 vback_porch;
    u32 vfront_porch;
    u32 vsync_len;
    bool ib2;
};

static const struct versatile_panel versatile_panels[] = {
    {
        .id = SYS_CLCD_ID_VGA,
        .compatible = "VGA",
        .clock_frequency = 25175000,
        .pixelclk_active = 0,
        .hsync_active = 1,
        .vsync_active = 1,
        .de_active = 1,
        .hactive = 640,
        .hback_porch = 48,
        .hfront_porch = 16,
        .hsync_len = 96,
        .vactive = 480,
        .vback_porch = 33,
        .vfront_porch = 10,
        .vsync_len = 2,
    },
    {
        .id = SYS_CLCD_ID_SANYO_3_8,
        .compatible = "sanyo,tm38qv67a02a",
        .clock_frequency = 10000000,
        .pixelclk_active = 1,
        .hsync_active = 1,
        .vsync_active = 1,
        .de_active = 1,
        .hactive = 320,
        .hback_porch = 6,
        .hfront_porch = 6,
        .hsync_len = 6,
        .vactive = 240,
        .vback_porch = 6,
        .vfront_porch = 6,
        .vsync_len = 0,
    },
    {
        .id = SYS_CLCD_ID_SHARP_8_4,
        .compatible = "sharp,lq084v1dg21",
        .clock_frequency = 25175000,
        .pixelclk_active = 0,
        .hsync_active = 1,
        .vsync_active = 1,
        .de_active = 1,
        .hactive = 640,
        .hback_porch = 48,
        .hfront_porch = 16,
        .hsync_len = 96,
        .vactive = 480,
        .vback_porch = 33,
        .vfront_porch = 10,
        .vsync_len = 2,
    },
    {
        .id = SYS_CLCD_ID_EPSON_2_2,
        .compatible = "epson,l2f50113t00",
        .clock_frequency = 16000000,
        .pixelclk_active = 0,
        .hsync_active = 1,
        .vsync_active = 1,
        .de_active = 1,
        .hactive = 176,
        .hback_porch = 3,
        .hfront_porch = 2,
        .hsync_len = 3,
        .vactive = 220,
        .vback_porch = 1,
        .vfront_porch = 0,
        .vsync_len = 2,
    },
    {
        .id = SYS_CLCD_ID_SANYO_2_5,
        .compatible = "sanyo,alr252rgt",
        .ib2 = true,
        .clock_frequency = 5440000,
        .pixelclk_active = 0,
        .hsync_active = 0,
        .vsync_active = 0,
        .de_active = 1,
        .hactive = 240,
        .hback_porch = 20,
        .hfront_porch = 10,
        .hsync_len = 10,
        .vactive = 320,
        .vback_porch = 2,
        .vfront_porch = 2,
        .vsync_len = 2,
    },
};

static void update_timings_prop(struct device *dev,
                struct of_changeset *cset,
                struct device_node *timings,
                const char *propname,
                u32 val)
{
    struct property *prop, *new;
    __be32 *dt_val;

    prop = of_find_property(timings, propname, NULL);
    if (!prop) {
        dev_err(dev, "could not find property \"%s\" - skipping\n",
            propname);
        return;
    }
    new = __of_prop_dup(prop, GFP_KERNEL);
    if (!new) {
        dev_err(dev, "could not copy property \"%s\" - skipping\n",
            propname);
        return;
    }

    dt_val = new->value;
    *dt_val = cpu_to_be32(val);

    of_changeset_update_property(cset, timings, new);
}

static int versatile_overwrite_of_panel(struct device *dev,
                    struct device_node *panel,
                    const struct versatile_panel *vpanel)
{
    struct of_changeset cset;
    struct device_node *timings;
    int ret;

    dev_info(dev, "CLCD: overwriting device tree\n");

    of_changeset_init(&cset);
    /* Find the timings node */
    timings = of_get_child_by_name(panel, "panel-timing");
    if (!timings) {
        dev_err(dev, "could not find panel timing node\n");
        goto err_destroy_cs;
    }
    update_timings_prop(dev, &cset, timings, "clock-frequency",
                vpanel->clock_frequency);
    update_timings_prop(dev, &cset, timings, "pixelclk-active",
                vpanel->pixelclk_active);
    update_timings_prop(dev, &cset, timings, "hsync-active",
                vpanel->hsync_active);
    update_timings_prop(dev, &cset, timings, "vsync-active",
                vpanel->vsync_active);
    update_timings_prop(dev, &cset, timings, "de-active",
                vpanel->de_active);
    update_timings_prop(dev, &cset, timings, "hactive",
                vpanel->hactive);
    update_timings_prop(dev, &cset, timings, "hback-porch",
                vpanel->hback_porch);
    update_timings_prop(dev, &cset, timings, "hsync-len",
                vpanel->hsync_len);
    update_timings_prop(dev, &cset, timings, "vactive",
                vpanel->vactive);
    update_timings_prop(dev, &cset, timings, "vback-porch",
                vpanel->vback_porch);
    update_timings_prop(dev, &cset, timings, "vfront-porch",
                vpanel->vfront_porch);
    update_timings_prop(dev, &cset, timings, "vsync-len",
                vpanel->hsync_len);

    ret = of_changeset_apply(&cset);
    if (ret) {
        dev_err(dev, "could not apply device tree changeset\n");
        goto err_destroy_cs;
    }
    return ret;

err_destroy_cs:
    of_changeset_destroy(&cset);
    return ret;
}

static void versatile_panel_probe(struct device *dev,
                  struct device_node *endpoint)
{
    struct versatile_panel const *vpanel = NULL;
    struct device_node *panel = NULL;
    u32 val;
    int ret;
    int i;

    /*
     * The Versatile CLCD has a panel auto-detection mechanism.
     * We use this and look for the compatible panel in the
     * device tree.
     */
    ret = regmap_read(versatile_syscon_map, SYS_CLCD, &val);
    if (ret) {
        dev_err(dev, "cannot read CLCD syscon register\n");
        return;
    }
    val &= SYS_CLCD_CLCDID_MASK;
    dev_info(dev, "SYS_CLCD=%08x\n", val);

    /* First find corresponding panel information */
    for (i = 0; i < ARRAY_SIZE(versatile_panels); i++) {
        vpanel = &versatile_panels[i];

        if (val == vpanel->id) {
            dev_err(dev, "autodetected panel \"%s\"\n",
                vpanel->compatible);
            break;
        }
    }
    if (i == ARRAY_SIZE(versatile_panels)) {
        dev_err(dev, "could not auto-detect panel\n");
        return;
    }

    /* This is the default panel node in the device tree */
    panel = of_graph_get_remote_port_parent(endpoint);
    if (!panel) {
        dev_err(dev, "could not locate remote port for panel\n");
        return;
    }

    /*
     * So now we dynamically update the display properties of the
     * panel in accordance to what was detected..
     */
    ret = versatile_overwrite_of_panel(dev, panel, vpanel);
    if (ret) {
        dev_err(dev, "cannot overwrite devicetree panel\n");
        return;
    }

    /*
     * If we have a Sanyo 2.5" port
     * that we're running on an IB2 and proceed to look for the
     * IB2 syscon regmap.
     */
    if (!vpanel->ib2)
        return;

    versatile_ib2_map = syscon_regmap_lookup_by_compatible(
        "arm,versatile-ib2-syscon");
    if (IS_ERR(versatile_ib2_map)) {
        dev_err(dev, "could not locate IB2 control register\n");
        versatile_ib2_map = NULL;
        return;
    }
}


This actually works. But would need some public device tree
property augmentation API instead of the hacks. Should I pursue
it?

Yours,
Linus Walleij



More information about the linux-arm-kernel mailing list