1 #include "fbtft.h"
2 #include "internal.h"
3 
4 static int get_next_ulong(char **str_p, unsigned long *val, char *sep, int base)
5 {
6 	char *p_val;
7 	int ret;
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 	ret = kstrtoul(p_val, base, val);
18 	if (ret)
19 		return -EINVAL;
20 
21 	return 0;
22 }
23 
24 int fbtft_gamma_parse_str(struct fbtft_par *par, unsigned long *curves,
25 						const char *str, int size)
26 {
27 	char *str_p, *curve_p = NULL;
28 	char *tmp;
29 	unsigned long val = 0;
30 	int ret = 0;
31 	int curve_counter, value_counter;
32 
33 	fbtft_par_dbg(DEBUG_SYSFS, par, "%s() str=\n", __func__);
34 
35 	if (!str || !curves)
36 		return -EINVAL;
37 
38 	fbtft_par_dbg(DEBUG_SYSFS, par, "%s\n", str);
39 
40 	tmp = kmemdup(str, size + 1, GFP_KERNEL);
41 	if (!tmp)
42 		return -ENOMEM;
43 
44 	/* replace optional separators */
45 	str_p = tmp;
46 	while (*str_p) {
47 		if (*str_p == ',')
48 			*str_p = ' ';
49 		if (*str_p == ';')
50 			*str_p = '\n';
51 		str_p++;
52 	}
53 
54 	str_p = strim(tmp);
55 
56 	curve_counter = 0;
57 	while (str_p) {
58 		if (curve_counter == par->gamma.num_curves) {
59 			dev_err(par->info->device, "Gamma: Too many curves\n");
60 			ret = -EINVAL;
61 			goto out;
62 		}
63 		curve_p = strsep(&str_p, "\n");
64 		value_counter = 0;
65 		while (curve_p) {
66 			if (value_counter == par->gamma.num_values) {
67 				dev_err(par->info->device,
68 					"Gamma: Too many values\n");
69 				ret = -EINVAL;
70 				goto out;
71 			}
72 			ret = get_next_ulong(&curve_p, &val, " ", 16);
73 			if (ret)
74 				goto out;
75 			curves[curve_counter * par->gamma.num_values + value_counter] = 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 
91 out:
92 	kfree(tmp);
93 	return ret;
94 }
95 
96 static ssize_t
97 sprintf_gamma(struct fbtft_par *par, unsigned long *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[len], PAGE_SIZE,
106 				"%04lx ", curves[i*par->gamma.num_values + j]);
107 		buf[len-1] = '\n';
108 	}
109 	mutex_unlock(&par->gamma.lock);
110 
111 	return len;
112 }
113 
114 static 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(device);
119 	struct fbtft_par *par = fb_info->par;
120 	unsigned long tmp_curves[FBTFT_GAMMA_MAX_VALUES_TOTAL];
121 	int ret;
122 
123 	ret = fbtft_gamma_parse_str(par, tmp_curves, buf, 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 * sizeof(tmp_curves[0]));
134 	mutex_unlock(&par->gamma.lock);
135 
136 	return count;
137 }
138 
139 static ssize_t show_gamma_curve(struct device *device,
140 				struct device_attribute *attr, char *buf)
141 {
142 	struct fb_info *fb_info = dev_get_drvdata(device);
143 	struct fbtft_par *par = fb_info->par;
144 
145 	return sprintf_gamma(par, par->gamma.curves, buf);
146 }
147 
148 static struct device_attribute gamma_device_attrs[] = {
149 	__ATTR(gamma, 0660, show_gamma_curve, store_gamma_curve),
150 };
151 
152 
153 void 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 
180 static 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(device);
185 	struct fbtft_par *par = fb_info->par;
186 	int ret;
187 
188 	ret = kstrtoul(buf, 10, &par->debug);
189 	if (ret)
190 		return ret;
191 	fbtft_expand_debug_value(&par->debug);
192 
193 	return count;
194 }
195 
196 static ssize_t show_debug(struct device *device,
197 				struct device_attribute *attr, char *buf)
198 {
199 	struct fb_info *fb_info = dev_get_drvdata(device);
200 	struct fbtft_par *par = fb_info->par;
201 
202 	return snprintf(buf, PAGE_SIZE, "%lu\n", par->debug);
203 }
204 
205 static struct device_attribute debug_device_attr = \
206 	__ATTR(debug, 0660, show_debug, store_debug);
207 
208 
209 void fbtft_sysfs_init(struct fbtft_par *par)
210 {
211 	device_create_file(par->info->dev, &debug_device_attr);
212 	if (par->gamma.curves && par->fbtftops.set_gamma)
213 		device_create_file(par->info->dev, &gamma_device_attrs[0]);
214 }
215 
216 void fbtft_sysfs_exit(struct fbtft_par *par)
217 {
218 	device_remove_file(par->info->dev, &debug_device_attr);
219 	if (par->gamma.curves && par->fbtftops.set_gamma)
220 		device_remove_file(par->info->dev, &gamma_device_attrs[0]);
221 }
222