6c483e3b23
Adding debug, info and error prefix for log messages in dsi files. To enable debug logs run "echo 0x1 > /sys/module/drm/parameters/debug" Change-Id: I438ac16954bd1d39450f8adeb7fb17f9ea6f8140 Signed-off-by: Satya Rama Aditya Pinapala <psraditya30@codeaurora.org>
1248 lines
24 KiB
C
1248 lines
24 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/device.h>
|
|
|
|
#include "dsi_parser.h"
|
|
#include "dsi_defs.h"
|
|
|
|
#define DSI_PARSER_MAX_NODES 20
|
|
|
|
enum dsi_parser_prop_type {
|
|
DSI_PROP_TYPE_STR,
|
|
DSI_PROP_TYPE_STR_ARRAY,
|
|
DSI_PROP_TYPE_INT_SET,
|
|
DSI_PROP_TYPE_INT_SET_ARRAY,
|
|
DSI_PROP_TYPE_INT_ARRAY,
|
|
};
|
|
|
|
struct dsi_parser_prop {
|
|
char *name;
|
|
char *raw;
|
|
char *value;
|
|
char **items;
|
|
enum dsi_parser_prop_type type;
|
|
int len;
|
|
};
|
|
|
|
struct dsi_parser_node {
|
|
char *name;
|
|
char *data;
|
|
|
|
struct dsi_parser_prop *prop;
|
|
int prop_count;
|
|
|
|
struct dsi_parser_node *child[DSI_PARSER_MAX_NODES];
|
|
int children_count;
|
|
};
|
|
|
|
struct dsi_parser {
|
|
const struct firmware *fw;
|
|
struct dsi_parser_node *head_node;
|
|
struct dsi_parser_node *current_node;
|
|
struct device *dev;
|
|
char *buf;
|
|
char file_name[SZ_32];
|
|
};
|
|
|
|
static int dsi_parser_count(char *buf, int item)
|
|
{
|
|
int count = 0;
|
|
|
|
do {
|
|
buf = strnchr(buf, strlen(buf), item);
|
|
if (buf)
|
|
count++;
|
|
} while (buf++);
|
|
|
|
return count;
|
|
}
|
|
|
|
static char *dsi_parser_clear_tail(char *buf)
|
|
{
|
|
int size = strlen(buf);
|
|
char *end;
|
|
|
|
if (!size)
|
|
goto exit;
|
|
|
|
end = buf + size - 1;
|
|
while (end >= buf && *end == '*')
|
|
end--;
|
|
|
|
*(end + 1) = '\0';
|
|
exit:
|
|
return buf;
|
|
}
|
|
|
|
static char *dsi_parser_strim(char *buf)
|
|
{
|
|
strreplace(buf, '*', ' ');
|
|
|
|
return strim(buf);
|
|
}
|
|
|
|
static char *dsi_parser_get_data(char *start, char *end, char *str)
|
|
{
|
|
strsep(&str, start);
|
|
if (str)
|
|
return dsi_parser_clear_tail(strsep(&str, end));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static bool dsi_parser_get_tuples_data(
|
|
struct dsi_parser_prop *prop, char *str)
|
|
{
|
|
bool middle_of_tx = false;
|
|
|
|
if (!str) {
|
|
DSI_ERR("Invalid input\n");
|
|
return middle_of_tx;
|
|
}
|
|
|
|
while (str) {
|
|
char *out = strsep(&str, " ");
|
|
|
|
if (str || middle_of_tx) {
|
|
middle_of_tx = true;
|
|
|
|
prop->items[prop->len++] = dsi_parser_strim(out);
|
|
}
|
|
}
|
|
|
|
return middle_of_tx;
|
|
}
|
|
|
|
static bool dsi_parser_get_strings(struct device *dev,
|
|
struct dsi_parser_prop *prop, char *str)
|
|
{
|
|
bool middle_of_tx = false;
|
|
int i = 0;
|
|
int count = 0;
|
|
|
|
if (!str) {
|
|
DSI_ERR("Invalid input\n");
|
|
goto end;
|
|
}
|
|
|
|
if (!dsi_parser_count(str, '"'))
|
|
goto end;
|
|
|
|
count = dsi_parser_count(str, ',');
|
|
DSI_DEBUG("count=%d\n", count);
|
|
|
|
if (!count) {
|
|
prop->value = dsi_parser_get_data("\"", "\"", str);
|
|
prop->type = DSI_PROP_TYPE_STR;
|
|
middle_of_tx = prop->value ? true : false;
|
|
|
|
goto end;
|
|
}
|
|
|
|
/* number of items are 1 more than separator */
|
|
count++;
|
|
prop->items = devm_kzalloc(dev, count, GFP_KERNEL);
|
|
if (!prop->items)
|
|
goto end;
|
|
|
|
prop->type = DSI_PROP_TYPE_STR_ARRAY;
|
|
|
|
while (str) {
|
|
char *out = strsep(&str, ",");
|
|
|
|
if ((str || middle_of_tx) && (i < count)) {
|
|
prop->items[i++] =
|
|
dsi_parser_get_data("\"", "\"", out);
|
|
prop->len++;
|
|
|
|
middle_of_tx = true;
|
|
}
|
|
}
|
|
end:
|
|
return middle_of_tx;
|
|
}
|
|
|
|
static bool dsi_parser_get_tuples(struct device *dev,
|
|
struct dsi_parser_prop *prop, char *str)
|
|
{
|
|
bool middle_of_tx = false;
|
|
char *data = NULL;
|
|
|
|
if (!str) {
|
|
DSI_ERR("Invalid input\n");
|
|
return middle_of_tx;
|
|
}
|
|
|
|
while (str) {
|
|
char *out = strsep(&str, ",");
|
|
|
|
if (str || middle_of_tx) {
|
|
data = dsi_parser_get_data("<", ">", out);
|
|
middle_of_tx = true;
|
|
|
|
dsi_parser_get_tuples_data(prop, data);
|
|
}
|
|
}
|
|
|
|
return middle_of_tx;
|
|
}
|
|
|
|
static void dsi_parser_get_int_value(struct dsi_parser_prop *prop,
|
|
int forced_base)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < prop->len; i++) {
|
|
int base, val;
|
|
char *to_int, *tmp;
|
|
char item[SZ_128];
|
|
|
|
strlcpy(item, prop->items[i], SZ_128);
|
|
|
|
tmp = item;
|
|
|
|
if (forced_base) {
|
|
base = forced_base;
|
|
} else {
|
|
to_int = strsep(&tmp, "x");
|
|
|
|
if (!tmp) {
|
|
tmp = to_int;
|
|
base = 10;
|
|
} else {
|
|
base = 16;
|
|
}
|
|
}
|
|
|
|
if (kstrtoint(tmp, base, &val)) {
|
|
DSI_ERR("error converting %s at %d\n",
|
|
tmp, i);
|
|
|
|
continue;
|
|
}
|
|
|
|
prop->value[i] = val & 0xFF;
|
|
}
|
|
}
|
|
|
|
static bool dsi_parser_parse_prop(struct device *dev,
|
|
struct dsi_parser_prop *prop, char *buf)
|
|
{
|
|
bool found = false;
|
|
char *out = strsep(&buf, "=");
|
|
|
|
if (!out || !buf)
|
|
goto end;
|
|
|
|
prop->raw = devm_kzalloc(dev, strlen(buf) + 1, GFP_KERNEL);
|
|
if (!prop->raw)
|
|
goto end;
|
|
|
|
strlcpy(prop->raw, buf, strlen(buf) + 1);
|
|
|
|
found = true;
|
|
|
|
prop->name = dsi_parser_strim(out);
|
|
DSI_DEBUG("RAW: %s: %s\n", prop->name, prop->raw);
|
|
|
|
prop->len = 0;
|
|
|
|
if (dsi_parser_get_strings(dev, prop, buf))
|
|
goto end;
|
|
|
|
prop->items = devm_kzalloc(dev, strlen(buf) * 2, GFP_KERNEL);
|
|
if (!prop->items)
|
|
goto end;
|
|
|
|
if (dsi_parser_get_tuples(dev, prop, buf)) {
|
|
prop->value = devm_kzalloc(dev, prop->len, GFP_KERNEL);
|
|
if (prop->value) {
|
|
prop->type = DSI_PROP_TYPE_INT_SET_ARRAY;
|
|
dsi_parser_get_int_value(prop, 0);
|
|
}
|
|
goto end;
|
|
}
|
|
|
|
prop->value = dsi_parser_get_data("<", ">", buf);
|
|
if (prop->value) {
|
|
if (dsi_parser_get_tuples_data(prop, prop->value)) {
|
|
prop->value = devm_kzalloc(dev, prop->len, GFP_KERNEL);
|
|
if (prop->value) {
|
|
prop->type = DSI_PROP_TYPE_INT_SET;
|
|
dsi_parser_get_int_value(prop, 0);
|
|
}
|
|
goto end;
|
|
} else {
|
|
prop->items[prop->len++] = prop->value;
|
|
}
|
|
|
|
goto end;
|
|
}
|
|
|
|
prop->value = dsi_parser_get_data("[", "]", buf);
|
|
if (prop->value) {
|
|
char *out5;
|
|
|
|
if (!prop->items)
|
|
goto end;
|
|
|
|
out5 = prop->value;
|
|
while (out5 && strlen(out5)) {
|
|
char *out6 = strsep(&out5, " ");
|
|
|
|
out6 = dsi_parser_strim(out6);
|
|
if (out6 && strlen(out6))
|
|
prop->items[prop->len++] = out6;
|
|
}
|
|
|
|
prop->value = devm_kzalloc(dev, prop->len, GFP_KERNEL);
|
|
if (prop->value) {
|
|
prop->type = DSI_PROP_TYPE_INT_ARRAY;
|
|
|
|
dsi_parser_get_int_value(prop, 16);
|
|
}
|
|
} else {
|
|
found = false;
|
|
}
|
|
end:
|
|
return found;
|
|
}
|
|
|
|
static char *dsi_parser_clean_name(char *name)
|
|
{
|
|
char *clean_name = name;
|
|
|
|
if (!name) {
|
|
DSI_ERR("Invalid input\n");
|
|
return NULL;
|
|
}
|
|
|
|
while (name)
|
|
clean_name = strsep(&name, ";");
|
|
|
|
return dsi_parser_strim(clean_name);
|
|
}
|
|
|
|
static char *dsi_parser_get_blob(char **buf, bool *has_child)
|
|
{
|
|
char *data = NULL;
|
|
char *start = *buf;
|
|
|
|
data = strpbrk(*buf, "{}");
|
|
if (!data)
|
|
goto end;
|
|
|
|
if (*data == '{')
|
|
*has_child = true;
|
|
|
|
if (*has_child) {
|
|
while (data != *buf) {
|
|
data--;
|
|
if (*data == ';') {
|
|
data++;
|
|
*data = '\0';
|
|
*buf = data + 1;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
*data = '\0';
|
|
*buf = data + 1;
|
|
}
|
|
end:
|
|
return start;
|
|
}
|
|
|
|
static struct dsi_parser_node *dsi_parser_find_nodes(struct device *dev,
|
|
char **buf)
|
|
{
|
|
struct dsi_parser_node *node = NULL, *cnode = NULL;
|
|
char *name, *data;
|
|
bool has_child = false;
|
|
|
|
if (!buf || !*buf)
|
|
goto end;
|
|
|
|
data = strpbrk(*buf, "{}");
|
|
if (!data) {
|
|
DSI_DEBUG("{} not found\n");
|
|
goto end;
|
|
}
|
|
|
|
if (*data == '}') {
|
|
*buf = data + 1;
|
|
goto end;
|
|
}
|
|
|
|
name = strsep(buf, "{");
|
|
|
|
if (*buf && name) {
|
|
node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL);
|
|
if (!node)
|
|
goto end;
|
|
|
|
node->name = dsi_parser_clean_name(name);
|
|
node->data = dsi_parser_get_blob(buf, &has_child);
|
|
|
|
if (!has_child)
|
|
goto end;
|
|
|
|
do {
|
|
cnode = dsi_parser_find_nodes(dev, buf);
|
|
if (cnode &&
|
|
(node->children_count < DSI_PARSER_MAX_NODES))
|
|
node->child[node->children_count++] = cnode;
|
|
} while (cnode);
|
|
}
|
|
end:
|
|
return node;
|
|
}
|
|
|
|
static void dsi_parser_count_properties(struct dsi_parser_node *node)
|
|
{
|
|
int count;
|
|
|
|
if (node && strlen(node->data)) {
|
|
node->prop_count = dsi_parser_count(node->data, ';');
|
|
|
|
for (count = 0; count < node->children_count; count++)
|
|
dsi_parser_count_properties(node->child[count]);
|
|
}
|
|
}
|
|
|
|
static void dsi_parser_get_properties(struct device *dev,
|
|
struct dsi_parser_node *node)
|
|
{
|
|
int count;
|
|
|
|
if (!node)
|
|
return;
|
|
|
|
if (node->prop_count) {
|
|
int i = 0;
|
|
char *buf = node->data;
|
|
|
|
node->prop = devm_kcalloc(dev, node->prop_count,
|
|
sizeof(struct dsi_parser_prop),
|
|
GFP_KERNEL);
|
|
if (!node->prop)
|
|
return;
|
|
|
|
for (i = 0; i < node->prop_count; i++) {
|
|
char *out = strsep(&buf, ";");
|
|
struct dsi_parser_prop *prop = &node->prop[i];
|
|
|
|
if (!out || !prop)
|
|
continue;
|
|
|
|
if (!dsi_parser_parse_prop(dev, prop, out)) {
|
|
char *out1 = strsep(&out, "}");
|
|
|
|
if (!out1)
|
|
continue;
|
|
|
|
out1 = dsi_parser_strim(out1);
|
|
|
|
if (!out && strlen(out1)) {
|
|
prop->name = out1;
|
|
prop->value = "1";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (count = 0; count < node->children_count; count++)
|
|
dsi_parser_get_properties(dev, node->child[count]);
|
|
}
|
|
|
|
static struct dsi_parser_prop *dsi_parser_search_property(
|
|
struct dsi_parser_node *node,
|
|
const char *name)
|
|
{
|
|
int i = 0;
|
|
struct dsi_parser_prop *prop = node->prop;
|
|
|
|
for (i = 0; i < node->prop_count; i++) {
|
|
if (prop[i].name && !strcmp(prop[i].name, name))
|
|
return &prop[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* APIs for the clients */
|
|
struct property *dsi_parser_find_property(const struct device_node *np,
|
|
const char *name,
|
|
int *lenp)
|
|
{
|
|
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
|
|
struct dsi_parser_prop *prop = NULL;
|
|
|
|
if (!node || !name || !lenp)
|
|
goto end;
|
|
|
|
prop = dsi_parser_search_property(node, name);
|
|
if (!prop) {
|
|
DSI_DEBUG("%s not found\n", name);
|
|
goto end;
|
|
}
|
|
|
|
if (lenp) {
|
|
if (prop->type == DSI_PROP_TYPE_INT_ARRAY)
|
|
*lenp = prop->len;
|
|
else if (prop->type == DSI_PROP_TYPE_INT_SET_ARRAY ||
|
|
prop->type == DSI_PROP_TYPE_INT_SET)
|
|
*lenp = prop->len * sizeof(u32);
|
|
else
|
|
*lenp = strlen(prop->raw) + 1;
|
|
|
|
DSI_DEBUG("%s len=%d\n", name, *lenp);
|
|
}
|
|
end:
|
|
return (struct property *)prop;
|
|
}
|
|
|
|
bool dsi_parser_read_bool(const struct device_node *np,
|
|
const char *propname)
|
|
{
|
|
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
|
|
bool prop_set;
|
|
|
|
prop_set = dsi_parser_search_property(node, propname) ? true : false;
|
|
|
|
DSI_DEBUG("%s=%s\n", propname, prop_set ? "set" : "not set");
|
|
|
|
return prop_set;
|
|
}
|
|
|
|
int dsi_parser_read_string(const struct device_node *np,
|
|
const char *propname, const char **out_string)
|
|
{
|
|
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
|
|
struct dsi_parser_prop *prop;
|
|
char *property = NULL;
|
|
int rc = 0;
|
|
|
|
prop = dsi_parser_search_property(node, propname);
|
|
if (!prop) {
|
|
DSI_DEBUG("%s not found\n", propname);
|
|
rc = -EINVAL;
|
|
} else {
|
|
property = prop->value;
|
|
}
|
|
|
|
*out_string = property;
|
|
|
|
DSI_DEBUG("%s=%s\n", propname, *out_string);
|
|
return rc;
|
|
}
|
|
|
|
int dsi_parser_read_u64(const struct device_node *np, const char *propname,
|
|
u64 *out_value)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
int dsi_parser_read_u32(const struct device_node *np,
|
|
const char *propname, u32 *out_value)
|
|
{
|
|
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
|
|
struct dsi_parser_prop *prop;
|
|
char *property, *to_int, item[SZ_128];
|
|
int rc = 0, base;
|
|
|
|
prop = dsi_parser_search_property(node, propname);
|
|
if (!prop) {
|
|
DSI_DEBUG("%s not found\n", propname);
|
|
rc = -EINVAL;
|
|
goto end;
|
|
}
|
|
|
|
if (!prop->value)
|
|
goto end;
|
|
|
|
strlcpy(item, prop->value, SZ_128);
|
|
property = item;
|
|
to_int = strsep(&property, "x");
|
|
|
|
if (!property) {
|
|
property = to_int;
|
|
base = 10;
|
|
} else {
|
|
base = 16;
|
|
}
|
|
|
|
rc = kstrtoint(property, base, out_value);
|
|
if (rc) {
|
|
DSI_ERR("prop=%s error(%d) converting %s, base=%d\n",
|
|
propname, rc, property, base);
|
|
goto end;
|
|
}
|
|
|
|
DSI_DEBUG("%s=%d\n", propname, *out_value);
|
|
end:
|
|
return rc;
|
|
}
|
|
|
|
int dsi_parser_read_u32_array(const struct device_node *np,
|
|
const char *propname,
|
|
u32 *out_values, size_t sz)
|
|
{
|
|
int i, rc = 0;
|
|
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
|
|
struct dsi_parser_prop *prop;
|
|
|
|
prop = dsi_parser_search_property(node, propname);
|
|
if (!prop) {
|
|
DSI_DEBUG("%s not found\n", propname);
|
|
rc = -EINVAL;
|
|
goto end;
|
|
}
|
|
|
|
for (i = 0; i < prop->len; i++) {
|
|
int base, val;
|
|
char item[SZ_128];
|
|
char *to_int, *tmp;
|
|
|
|
strlcpy(item, prop->items[i], SZ_128);
|
|
|
|
tmp = item;
|
|
|
|
to_int = strsep(&tmp, "x");
|
|
|
|
if (!tmp) {
|
|
tmp = to_int;
|
|
base = 10;
|
|
} else {
|
|
base = 16;
|
|
}
|
|
|
|
rc = kstrtoint(tmp, base, &val);
|
|
if (rc) {
|
|
DSI_ERR("prop=%s error(%d) converting %s(%d), base=%d\n",
|
|
propname, rc, tmp, i, base);
|
|
continue;
|
|
}
|
|
|
|
*out_values++ = val;
|
|
|
|
DSI_DEBUG("%s: [%d]=%d\n", propname, i, *(out_values - 1));
|
|
}
|
|
end:
|
|
return rc;
|
|
}
|
|
|
|
const void *dsi_parser_get_property(const struct device_node *np,
|
|
const char *name, int *lenp)
|
|
{
|
|
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
|
|
struct dsi_parser_prop *prop;
|
|
char *property = NULL;
|
|
|
|
prop = dsi_parser_search_property(node, name);
|
|
if (!prop) {
|
|
DSI_DEBUG("%s not found\n", name);
|
|
goto end;
|
|
}
|
|
|
|
property = prop->value;
|
|
|
|
if (prop->type == DSI_PROP_TYPE_STR)
|
|
DSI_DEBUG("%s=%s\n", name, property);
|
|
|
|
if (lenp) {
|
|
if (prop->type == DSI_PROP_TYPE_INT_ARRAY)
|
|
*lenp = prop->len;
|
|
else if (prop->type == DSI_PROP_TYPE_INT_SET_ARRAY ||
|
|
prop->type == DSI_PROP_TYPE_INT_SET)
|
|
*lenp = prop->len * sizeof(u32);
|
|
else
|
|
*lenp = strlen(prop->raw) + 1;
|
|
|
|
DSI_DEBUG("%s len=%d\n", name, *lenp);
|
|
}
|
|
end:
|
|
return property;
|
|
}
|
|
|
|
struct device_node *dsi_parser_get_child_by_name(const struct device_node *np,
|
|
const char *name)
|
|
{
|
|
int index = 0;
|
|
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
|
|
struct dsi_parser_node *matched_node = NULL;
|
|
|
|
if (!node || !node->children_count)
|
|
goto end;
|
|
|
|
do {
|
|
struct dsi_parser_node *child_node = node->child[index++];
|
|
|
|
if (!child_node)
|
|
goto end;
|
|
|
|
if (!strcmp(child_node->name, name)) {
|
|
matched_node = child_node;
|
|
break;
|
|
}
|
|
} while (index < node->children_count);
|
|
end:
|
|
DSI_DEBUG("%s: %s\n", name, matched_node ? "found" : "not found");
|
|
|
|
return (struct device_node *)matched_node;
|
|
}
|
|
|
|
struct dsi_parser_node *dsi_parser_get_node_by_name(
|
|
struct dsi_parser_node *node,
|
|
char *name)
|
|
{
|
|
int count = 0;
|
|
struct dsi_parser_node *matched_node = NULL;
|
|
|
|
if (!node) {
|
|
DSI_ERR("node is null\n");
|
|
goto end;
|
|
}
|
|
|
|
if (!strcmp(node->name, name)) {
|
|
matched_node = node;
|
|
goto end;
|
|
}
|
|
|
|
for (count = 0; count < node->children_count; count++) {
|
|
matched_node = dsi_parser_get_node_by_name(
|
|
node->child[count], name);
|
|
if (matched_node)
|
|
break;
|
|
}
|
|
end:
|
|
DSI_DEBUG("%s: %s\n", name, matched_node ? "found" : "not found");
|
|
|
|
return matched_node;
|
|
}
|
|
|
|
int dsi_parser_get_child_count(const struct device_node *np)
|
|
{
|
|
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
|
|
int count = 0;
|
|
|
|
if (node) {
|
|
count = node->children_count;
|
|
DSI_DEBUG("node %s child count=%d\n", node->name, count);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
struct device_node *dsi_parser_get_next_child(const struct device_node *np,
|
|
struct device_node *prev)
|
|
{
|
|
int index = 0;
|
|
struct dsi_parser_node *parent = (struct dsi_parser_node *)np;
|
|
struct dsi_parser_node *prev_child = (struct dsi_parser_node *)prev;
|
|
struct dsi_parser_node *matched_node = NULL;
|
|
|
|
if (!parent || !parent->children_count)
|
|
goto end;
|
|
|
|
do {
|
|
struct dsi_parser_node *child_node = parent->child[index++];
|
|
|
|
if (!child_node)
|
|
goto end;
|
|
|
|
if (!prev) {
|
|
matched_node = child_node;
|
|
goto end;
|
|
}
|
|
|
|
if (!strcmp(child_node->name, prev_child->name)) {
|
|
if (index < parent->children_count)
|
|
matched_node = parent->child[index];
|
|
break;
|
|
}
|
|
} while (index < parent->children_count);
|
|
end:
|
|
if (matched_node)
|
|
DSI_DEBUG("next child: %s\n", matched_node->name);
|
|
|
|
return (struct device_node *)matched_node;
|
|
}
|
|
|
|
int dsi_parser_count_u32_elems(const struct device_node *np,
|
|
const char *propname)
|
|
{
|
|
int count = 0;
|
|
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
|
|
struct dsi_parser_prop *prop;
|
|
|
|
prop = dsi_parser_search_property(node, propname);
|
|
if (!prop) {
|
|
DSI_DEBUG("%s not found\n", propname);
|
|
goto end;
|
|
}
|
|
|
|
count = prop->len;
|
|
|
|
DSI_DEBUG("prop %s has %d items\n", prop->name, count);
|
|
end:
|
|
return count;
|
|
}
|
|
|
|
int dsi_parser_count_strings(const struct device_node *np,
|
|
const char *propname)
|
|
{
|
|
int count = 0;
|
|
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
|
|
struct dsi_parser_prop *prop;
|
|
|
|
prop = dsi_parser_search_property(node, propname);
|
|
if (!prop) {
|
|
DSI_DEBUG("%s not found\n", propname);
|
|
goto end;
|
|
}
|
|
|
|
if (prop->type == DSI_PROP_TYPE_STR_ARRAY)
|
|
count = prop->len;
|
|
else if (prop->type == DSI_PROP_TYPE_STR)
|
|
count = 1;
|
|
|
|
DSI_DEBUG("prop %s has %d items\n", prop->name, count);
|
|
end:
|
|
return count;
|
|
}
|
|
|
|
int dsi_parser_read_string_index(const struct device_node *np,
|
|
const char *propname,
|
|
int index, const char **output)
|
|
{
|
|
struct dsi_parser_node *node = (struct dsi_parser_node *)np;
|
|
struct dsi_parser_prop *prop;
|
|
|
|
prop = dsi_parser_search_property(node, propname);
|
|
if (!prop) {
|
|
DSI_DEBUG("%s not found\n", propname);
|
|
goto end;
|
|
}
|
|
|
|
if (prop->type != DSI_PROP_TYPE_STR_ARRAY) {
|
|
DSI_ERR("not a string array property\n");
|
|
goto end;
|
|
}
|
|
|
|
if (index >= prop->len) {
|
|
DSI_ERR("out of bond index %d\n", index);
|
|
goto end;
|
|
}
|
|
|
|
*output = prop->items[index];
|
|
|
|
return 0;
|
|
end:
|
|
return -EINVAL;
|
|
}
|
|
|
|
int dsi_parser_get_named_gpio(struct device_node *np,
|
|
const char *propname, int index)
|
|
{
|
|
int gpio = -EINVAL;
|
|
|
|
dsi_parser_read_u32(np, propname, &gpio);
|
|
|
|
return gpio;
|
|
}
|
|
|
|
void *dsi_parser_get_head_node(void *in,
|
|
const u8 *data, u32 size)
|
|
{
|
|
struct dsi_parser *parser = in;
|
|
char *buf;
|
|
|
|
if (!parser || !data || !size) {
|
|
DSI_ERR("invalid input\n");
|
|
goto err;
|
|
}
|
|
|
|
parser->buf = devm_kzalloc(parser->dev, size, GFP_KERNEL);
|
|
if (!parser->buf)
|
|
goto err;
|
|
|
|
buf = parser->buf;
|
|
|
|
memcpy(buf, data, size);
|
|
|
|
strreplace(buf, '\n', ' ');
|
|
strreplace(buf, '\t', '*');
|
|
|
|
parser->head_node = dsi_parser_find_nodes(parser->dev, &buf);
|
|
if (!parser->head_node) {
|
|
DSI_ERR("could not get head node\n");
|
|
devm_kfree(parser->dev, parser->buf);
|
|
goto err;
|
|
}
|
|
|
|
dsi_parser_count_properties(parser->head_node);
|
|
dsi_parser_get_properties(parser->dev, parser->head_node);
|
|
|
|
parser->current_node = parser->head_node;
|
|
|
|
return parser->head_node;
|
|
err:
|
|
return NULL;
|
|
}
|
|
|
|
static int dsi_parser_read_file(struct dsi_parser *parser,
|
|
const u8 **buf, u32 *size)
|
|
{
|
|
int rc = 0;
|
|
|
|
release_firmware(parser->fw);
|
|
|
|
rc = request_firmware(&parser->fw, parser->file_name, parser->dev);
|
|
if (rc || !parser->fw) {
|
|
DSI_ERR("couldn't read firmware\n");
|
|
goto end;
|
|
}
|
|
|
|
*buf = parser->fw->data;
|
|
*size = parser->fw->size;
|
|
|
|
DSI_DEBUG("file %s: size %zd\n",
|
|
parser->file_name, parser->fw->size);
|
|
end:
|
|
return rc;
|
|
}
|
|
|
|
static void dsi_parser_free_mem(struct device *dev,
|
|
struct dsi_parser_node *node)
|
|
{
|
|
int i = 0;
|
|
|
|
if (!node)
|
|
return;
|
|
|
|
DSI_DEBUG("node=%s, prop_count=%d\n", node->name, node->prop_count);
|
|
|
|
for (i = 0; i < node->prop_count; i++) {
|
|
struct dsi_parser_prop *prop = &node->prop[i];
|
|
|
|
if (!prop)
|
|
continue;
|
|
|
|
DSI_DEBUG("deleting prop=%s\n", prop->name);
|
|
|
|
if (prop->items)
|
|
devm_kfree(dev, prop->items);
|
|
|
|
if (prop->raw)
|
|
devm_kfree(dev, prop->raw);
|
|
|
|
if ((prop->type == DSI_PROP_TYPE_INT_SET_ARRAY ||
|
|
prop->type == DSI_PROP_TYPE_INT_SET ||
|
|
prop->type == DSI_PROP_TYPE_INT_ARRAY) && prop->value)
|
|
devm_kfree(dev, prop->value);
|
|
}
|
|
|
|
if (node->prop)
|
|
devm_kfree(dev, node->prop);
|
|
|
|
for (i = 0; i < node->children_count; i++)
|
|
dsi_parser_free_mem(dev, node->child[i]);
|
|
|
|
devm_kfree(dev, node);
|
|
}
|
|
|
|
static ssize_t dsi_parser_write_init(struct file *file,
|
|
const char __user *user_buff, size_t count, loff_t *ppos)
|
|
{
|
|
struct dsi_parser *parser = file->private_data;
|
|
const u8 *data = NULL;
|
|
u32 size = 0;
|
|
char buf[SZ_32];
|
|
size_t len = 0;
|
|
|
|
if (!parser)
|
|
return -ENODEV;
|
|
|
|
if (*ppos)
|
|
return 0;
|
|
|
|
/* Leave room for termination char */
|
|
len = min_t(size_t, count, SZ_32 - 1);
|
|
if (copy_from_user(buf, user_buff, len))
|
|
goto end;
|
|
|
|
buf[len] = '\0';
|
|
|
|
if (sscanf(buf, "%31s", parser->file_name) != 1) {
|
|
DSI_ERR("failed to get val\n");
|
|
goto end;
|
|
}
|
|
|
|
if (dsi_parser_read_file(parser, &data, &size)) {
|
|
DSI_ERR("failed to read file\n");
|
|
goto end;
|
|
}
|
|
|
|
dsi_parser_free_mem(parser->dev, parser->head_node);
|
|
|
|
if (parser->buf) {
|
|
devm_kfree(parser->dev, parser->buf);
|
|
parser->buf = NULL;
|
|
}
|
|
|
|
parser->head_node = dsi_parser_get_head_node(parser, data, size);
|
|
if (!parser->head_node) {
|
|
DSI_ERR("failed to parse data\n");
|
|
goto end;
|
|
}
|
|
end:
|
|
return len;
|
|
}
|
|
|
|
static ssize_t dsi_parser_read_node(struct file *file,
|
|
char __user *user_buff, size_t count, loff_t *ppos)
|
|
{
|
|
char *buf = NULL;
|
|
int i, j, len = 0, max_size = SZ_4K;
|
|
struct dsi_parser *parser = file->private_data;
|
|
struct dsi_parser_node *node;
|
|
struct dsi_parser_prop *prop;
|
|
|
|
if (!parser)
|
|
return -ENODEV;
|
|
|
|
if (*ppos)
|
|
return len;
|
|
|
|
buf = kzalloc(SZ_4K, GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
node = parser->current_node;
|
|
if (!node) {
|
|
len = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
prop = node->prop;
|
|
|
|
len += scnprintf(buf + len, max_size - len, "node name=%s\n",
|
|
node->name);
|
|
if (len == max_size)
|
|
goto buffer_overflow;
|
|
|
|
len += scnprintf(buf + len, max_size - len, "children count=%d\n",
|
|
node->children_count);
|
|
if (len == max_size)
|
|
goto buffer_overflow;
|
|
|
|
for (i = 0; i < node->children_count; i++) {
|
|
len += scnprintf(buf + len, max_size - len, "child[%d]=%s\n",
|
|
i, node->child[i]->name);
|
|
if (len == max_size)
|
|
goto buffer_overflow;
|
|
}
|
|
|
|
for (i = 0; i < node->prop_count; i++) {
|
|
if (!prop[i].name)
|
|
continue;
|
|
|
|
len += scnprintf(buf + len, max_size - len,
|
|
"property=%s\n", prop[i].name);
|
|
if (len == max_size)
|
|
goto buffer_overflow;
|
|
|
|
if (prop[i].value) {
|
|
if (prop[i].type == DSI_PROP_TYPE_STR) {
|
|
len += scnprintf(buf + len, max_size - len,
|
|
"value=%s\n", prop[i].value);
|
|
if (len == max_size)
|
|
goto buffer_overflow;
|
|
} else {
|
|
for (j = 0; j < prop[i].len; j++) {
|
|
len += scnprintf(buf + len,
|
|
max_size - len,
|
|
"%x", prop[i].value[j]);
|
|
if (len == max_size)
|
|
goto buffer_overflow;
|
|
}
|
|
|
|
len += scnprintf(buf + len, max_size - len,
|
|
"\n");
|
|
if (len == max_size)
|
|
goto buffer_overflow;
|
|
|
|
}
|
|
}
|
|
|
|
if (prop[i].len) {
|
|
len += scnprintf(buf + len, max_size - len, "items:\n");
|
|
if (len == max_size)
|
|
goto buffer_overflow;
|
|
}
|
|
|
|
for (j = 0; j < prop[i].len; j++) {
|
|
char delim;
|
|
|
|
if (j && !(j % 10))
|
|
delim = '\n';
|
|
else
|
|
delim = ' ';
|
|
|
|
len += scnprintf(buf + len, max_size - len, "%s%c",
|
|
prop[i].items[j], delim);
|
|
if (len == max_size)
|
|
goto buffer_overflow;
|
|
}
|
|
|
|
len += scnprintf(buf + len, max_size - len, "\n\n");
|
|
if (len == max_size)
|
|
goto buffer_overflow;
|
|
}
|
|
buffer_overflow:
|
|
if (simple_read_from_buffer(user_buff, count, ppos, buf, len)) {
|
|
len = -EFAULT;
|
|
goto error;
|
|
}
|
|
error:
|
|
kfree(buf);
|
|
|
|
return len;
|
|
}
|
|
|
|
static ssize_t dsi_parser_write_node(struct file *file,
|
|
const char __user *user_buff, size_t count, loff_t *ppos)
|
|
{
|
|
struct dsi_parser *parser = file->private_data;
|
|
char buf[SZ_512];
|
|
size_t len = 0;
|
|
|
|
if (!parser)
|
|
return -ENODEV;
|
|
|
|
if (*ppos)
|
|
return 0;
|
|
|
|
/* Leave room for termination char */
|
|
len = min_t(size_t, count, SZ_512 - 1);
|
|
if (copy_from_user(buf, user_buff, len))
|
|
goto end;
|
|
|
|
buf[len] = '\0';
|
|
|
|
strreplace(buf, '\n', ' ');
|
|
|
|
if (!strcmp(strim(buf), "head_node"))
|
|
parser->current_node = parser->head_node;
|
|
else
|
|
parser->current_node = dsi_parser_get_node_by_name(
|
|
parser->head_node, strim(buf));
|
|
end:
|
|
return len;
|
|
}
|
|
|
|
static const struct file_operations dsi_parser_init_fops = {
|
|
.open = simple_open,
|
|
.write = dsi_parser_write_init,
|
|
};
|
|
|
|
static const struct file_operations dsi_parser_node_fops = {
|
|
.open = simple_open,
|
|
.read = dsi_parser_read_node,
|
|
.write = dsi_parser_write_node,
|
|
};
|
|
|
|
int dsi_parser_dbg_init(void *parser, struct dentry *parent_dir)
|
|
{
|
|
int rc = 0;
|
|
struct dentry *dir, *file;
|
|
|
|
if (!parser || !parent_dir) {
|
|
DSI_ERR("invalid input\n");
|
|
goto end;
|
|
}
|
|
|
|
dir = debugfs_create_dir("parser", parent_dir);
|
|
if (IS_ERR_OR_NULL(dir)) {
|
|
rc = PTR_ERR(dir);
|
|
|
|
DSI_ERR("failed to create parser debugfs\n");
|
|
goto end;
|
|
}
|
|
|
|
file = debugfs_create_file("init", 0644, dir,
|
|
parser, &dsi_parser_init_fops);
|
|
if (IS_ERR_OR_NULL(file)) {
|
|
rc = PTR_ERR(file);
|
|
|
|
DSI_ERR("failed to create init debugfs\n");
|
|
goto dbg;
|
|
}
|
|
|
|
file = debugfs_create_file("node", 0644, dir,
|
|
parser, &dsi_parser_node_fops);
|
|
if (IS_ERR_OR_NULL(file)) {
|
|
rc = PTR_ERR(file);
|
|
|
|
DSI_ERR("failed to create init debugfs\n");
|
|
goto dbg;
|
|
}
|
|
|
|
DSI_DEBUG("success\n");
|
|
return 0;
|
|
dbg:
|
|
debugfs_remove_recursive(dir);
|
|
end:
|
|
return rc;
|
|
}
|
|
|
|
void *dsi_parser_get(struct device *dev)
|
|
{
|
|
int rc = 0;
|
|
struct dsi_parser *parser = NULL;
|
|
|
|
if (!dev) {
|
|
DSI_ERR("invalid data\n");
|
|
rc = -EINVAL;
|
|
goto end;
|
|
}
|
|
|
|
parser = devm_kzalloc(dev, sizeof(*parser), GFP_KERNEL);
|
|
if (!parser) {
|
|
rc = -ENOMEM;
|
|
goto end;
|
|
}
|
|
|
|
parser->dev = dev;
|
|
|
|
strlcpy(parser->file_name, "dsi_prop", sizeof(parser->file_name));
|
|
|
|
return parser;
|
|
end:
|
|
return ERR_PTR(rc);
|
|
}
|
|
|
|
void dsi_parser_put(void *data)
|
|
{
|
|
struct dsi_parser *parser = data;
|
|
|
|
if (!parser)
|
|
return;
|
|
|
|
dsi_parser_free_mem(parser->dev, parser->head_node);
|
|
|
|
devm_kfree(parser->dev, parser->buf);
|
|
devm_kfree(parser->dev, parser);
|
|
}
|
|
|