xref: /linux/drivers/staging/fbtft/fbtft-sysfs.c (revision 44f57d78)
1 // SPDX-License-Identifier: GPL-2.0
2 #include "fbtft.h"
3 #include "internal.h"
4 
5 static int get_next_ulong(char **str_p, unsigned long *val, char *sep, int base)
6 {
7 	char *p_val;
8 
9 	if (!str_p || !(*str_p))
10 		return -EINVAL;
11 
12 	p_val = strsep(str_p, sep);
13 
14 	if (!p_val)
15 		return -EINVAL;
16 
17 	return kstrtoul(p_val, base, val);
18 }
19 
20 int fbtft_gamma_parse_str(struct fbtft_par *par, u32 *curves,
21 			  const char *str, int size)
22 {
23 	char *str_p, *curve_p = NULL;
24 	char *tmp;
25 	unsigned long val = 0;
26 	int ret = 0;
27 	int curve_counter, value_counter;
28 
29 	fbtft_par_dbg(DEBUG_SYSFS, par, "%s() str=\n", __func__);
30 
31 	if (!str || !curves)
32 		return -EINVAL;
33 
34 	fbtft_par_dbg(DEBUG_SYSFS, par, "%s\n", str);
35 
36 	tmp = kmemdup(str, size + 1, GFP_KERNEL);
37 	if (!tmp)
38 		return -ENOMEM;
39 
40 	/* replace optional separators */
41 	str_p = tmp;
42 	while (*str_p) {
43 		if (*str_p == ',')
44 			*str_p = ' ';
45 		if (*str_p == ';')
46 			*str_p = '\n';
47 		str_p++;
48 	}
49 
50 	str_p = strim(tmp);
51 
52 	curve_counter = 0;
53 	while (str_p) {
54 		if (curve_counter == par->gamma.num_curves) {
55 			dev_err(par->info->device, "Gamma: Too many curves\n");
56 			ret = -EINVAL;
57 			goto out;
58 		}
59 		curve_p = strsep(&str_p, "\n");
60 		value_counter = 0;
61 		while (curve_p) {
62 			if (value_counter == par->gamma.num_values) {
63 				dev_err(par->info->device,
64 					"Gamma: Too many values\n");
65 				ret = -EINVAL;
66 				goto out;
67 			}
68 			ret = get_next_ulong(&curve_p, &val, " ", 16);
69 			if (ret)
70 				goto out;
71 			curves[curve_counter * par->gamma.num_values + value_counter] = val;
72 			value_counter++;
73 		}
74 		if (value_counter != par->gamma.num_values) {
75 			dev_err(par->info->device, "Gamma: Too few values\n");
76 			ret = -EINVAL;
77 			goto out;
78 		}
79 		curve_counter++;
80 	}
81 	if (curve_counter != par->gamma.num_curves) {
82 		dev_err(par->info->device, "Gamma: Too few curves\n");
83 		ret = -EINVAL;
84 		goto out;
85 	}
86 
87 out:
88 	kfree(tmp);
89 	return ret;
90 }
91 
92 static ssize_t
93 sprintf_gamma(struct fbtft_par *par, u32 *curves, char *buf)
94 {
95 	ssize_t len = 0;
96 	unsigned int i, j;
97 
98 	mutex_lock(&par->gamma.lock);
99 	for (i = 0; i < par->gamma.num_curves; i++) {
100 		for (j = 0; j < par->gamma.num_values; j++)
101 			len += scnprintf(&buf[len], PAGE_SIZE,
102 			     "%04x ", curves[i * par->gamma.num_values + j]);
103 		buf[len - 1] = '\n';
104 	}
105 	mutex_unlock(&par->gamma.lock);
106 
107 	return len;
108 }
109 
110 static ssize_t store_gamma_curve(struct device *device,
111 				 struct device_attribute *attr,
112 				 const char *buf, size_t count)
113 {
114 	struct fb_info *fb_info = dev_get_drvdata(device);
115 	struct fbtft_par *par = fb_info->par;
116 	u32 tmp_curves[FBTFT_GAMMA_MAX_VALUES_TOTAL];
117 	int ret;
118 
119 	ret = fbtft_gamma_parse_str(par, tmp_curves, buf, count);
120 	if (ret)
121 		return ret;
122 
123 	ret = par->fbtftops.set_gamma(par, tmp_curves);
124 	if (ret)
125 		return ret;
126 
127 	mutex_lock(&par->gamma.lock);
128 	memcpy(par->gamma.curves, tmp_curves,
129 	       par->gamma.num_curves * par->gamma.num_values *
130 	       sizeof(tmp_curves[0]));
131 	mutex_unlock(&par->gamma.lock);
132 
133 	return count;
134 }
135 
136 static ssize_t show_gamma_curve(struct device *device,
137 				struct device_attribute *attr, char *buf)
138 {
139 	struct fb_info *fb_info = dev_get_drvdata(device);
140 	struct fbtft_par *par = fb_info->par;
141 
142 	return sprintf_gamma(par, par->gamma.curves, buf);
143 }
144 
145 static struct device_attribute gamma_device_attrs[] = {
146 	__ATTR(gamma, 0660, show_gamma_curve, store_gamma_curve),
147 };
148 
149 void fbtft_expand_debug_value(unsigned long *debug)
150 {
151 	switch (*debug & 0x7) {
152 	case 1:
153 		*debug |= DEBUG_LEVEL_1;
154 		break;
155 	case 2:
156 		*debug |= DEBUG_LEVEL_2;
157 		break;
158 	case 3:
159 		*debug |= DEBUG_LEVEL_3;
160 		break;
161 	case 4:
162 		*debug |= DEBUG_LEVEL_4;
163 		break;
164 	case 5:
165 		*debug |= DEBUG_LEVEL_5;
166 		break;
167 	case 6:
168 		*debug |= DEBUG_LEVEL_6;
169 		break;
170 	case 7:
171 		*debug = 0xFFFFFFFF;
172 		break;
173 	}
174 }
175 
176 static ssize_t store_debug(struct device *device,
177 			   struct device_attribute *attr,
178 			   const char *buf, size_t count)
179 {
180 	struct fb_info *fb_info = dev_get_drvdata(device);
181 	struct fbtft_par *par = fb_info->par;
182 	int ret;
183 
184 	ret = kstrtoul(buf, 10, &par->debug);
185 	if (ret)
186 		return ret;
187 	fbtft_expand_debug_value(&par->debug);
188 
189 	return count;
190 }
191 
192 static ssize_t show_debug(struct device *device,
193 			  struct device_attribute *attr, char *buf)
194 {
195 	struct fb_info *fb_info = dev_get_drvdata(device);
196 	struct fbtft_par *par = fb_info->par;
197 
198 	return snprintf(buf, PAGE_SIZE, "%lu\n", par->debug);
199 }
200 
201 static struct device_attribute debug_device_attr =
202 	__ATTR(debug, 0660, show_debug, store_debug);
203 
204 void fbtft_sysfs_init(struct fbtft_par *par)
205 {
206 	device_create_file(par->info->dev, &debug_device_attr);
207 	if (par->gamma.curves && par->fbtftops.set_gamma)
208 		device_create_file(par->info->dev, &gamma_device_attrs[0]);
209 }
210 
211 void fbtft_sysfs_exit(struct fbtft_par *par)
212 {
213 	device_remove_file(par->info->dev, &debug_device_attr);
214 	if (par->gamma.curves && par->fbtftops.set_gamma)
215 		device_remove_file(par->info->dev, &gamma_device_attrs[0]);
216 }
217