GNU Linux-libre 4.19.268-gnu1
[releases.git] / drivers / staging / fbtft / fbtft-sysfs.c
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 }