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

source code of linux/drivers/staging/fbtft/fbtft-sysfs.c