1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2009 Nokia Corporation
4 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
5 *
6 * Some code and ideas taken from drivers/video/omap/ driver
7 * by Imre Deak.
8 */
9
10 #define DSS_SUBSYS_NAME "DISPLAY"
11
12 #include <linux/kernel.h>
13 #include <linux/kstrtox.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/sysfs.h>
17
18 #include <video/omapfb_dss.h>
19 #include "dss.h"
20
display_name_show(struct omap_dss_device * dssdev,char * buf)21 static ssize_t display_name_show(struct omap_dss_device *dssdev, char *buf)
22 {
23 return sysfs_emit(buf, "%s\n",
24 dssdev->name ?
25 dssdev->name : "");
26 }
27
display_enabled_show(struct omap_dss_device * dssdev,char * buf)28 static ssize_t display_enabled_show(struct omap_dss_device *dssdev, char *buf)
29 {
30 return sysfs_emit(buf, "%d\n",
31 omapdss_device_is_enabled(dssdev));
32 }
33
display_enabled_store(struct omap_dss_device * dssdev,const char * buf,size_t size)34 static ssize_t display_enabled_store(struct omap_dss_device *dssdev,
35 const char *buf, size_t size)
36 {
37 int r;
38 bool enable;
39
40 r = kstrtobool(buf, &enable);
41 if (r)
42 return r;
43
44 if (enable == omapdss_device_is_enabled(dssdev))
45 return size;
46
47 if (omapdss_device_is_connected(dssdev) == false)
48 return -ENODEV;
49
50 if (enable) {
51 r = dssdev->driver->enable(dssdev);
52 if (r)
53 return r;
54 } else {
55 dssdev->driver->disable(dssdev);
56 }
57
58 return size;
59 }
60
display_tear_show(struct omap_dss_device * dssdev,char * buf)61 static ssize_t display_tear_show(struct omap_dss_device *dssdev, char *buf)
62 {
63 return sysfs_emit(buf, "%d\n",
64 dssdev->driver->get_te ?
65 dssdev->driver->get_te(dssdev) : 0);
66 }
67
display_tear_store(struct omap_dss_device * dssdev,const char * buf,size_t size)68 static ssize_t display_tear_store(struct omap_dss_device *dssdev,
69 const char *buf, size_t size)
70 {
71 int r;
72 bool te;
73
74 if (!dssdev->driver->enable_te || !dssdev->driver->get_te)
75 return -ENOENT;
76
77 r = kstrtobool(buf, &te);
78 if (r)
79 return r;
80
81 r = dssdev->driver->enable_te(dssdev, te);
82 if (r)
83 return r;
84
85 return size;
86 }
87
display_timings_show(struct omap_dss_device * dssdev,char * buf)88 static ssize_t display_timings_show(struct omap_dss_device *dssdev, char *buf)
89 {
90 struct omap_video_timings t;
91
92 if (!dssdev->driver->get_timings)
93 return -ENOENT;
94
95 dssdev->driver->get_timings(dssdev, &t);
96
97 return sysfs_emit(buf, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n",
98 t.pixelclock,
99 t.x_res, t.hfp, t.hbp, t.hsw,
100 t.y_res, t.vfp, t.vbp, t.vsw);
101 }
102
display_timings_store(struct omap_dss_device * dssdev,const char * buf,size_t size)103 static ssize_t display_timings_store(struct omap_dss_device *dssdev,
104 const char *buf, size_t size)
105 {
106 struct omap_video_timings t = dssdev->panel.timings;
107 int r, found;
108
109 if (!dssdev->driver->set_timings || !dssdev->driver->check_timings)
110 return -ENOENT;
111
112 found = 0;
113 #ifdef CONFIG_FB_OMAP2_DSS_VENC
114 if (strncmp("pal", buf, 3) == 0) {
115 t = omap_dss_pal_timings;
116 found = 1;
117 } else if (strncmp("ntsc", buf, 4) == 0) {
118 t = omap_dss_ntsc_timings;
119 found = 1;
120 }
121 #endif
122 if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu",
123 &t.pixelclock,
124 &t.x_res, &t.hfp, &t.hbp, &t.hsw,
125 &t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9)
126 return -EINVAL;
127
128 r = dssdev->driver->check_timings(dssdev, &t);
129 if (r)
130 return r;
131
132 dssdev->driver->disable(dssdev);
133 dssdev->driver->set_timings(dssdev, &t);
134 r = dssdev->driver->enable(dssdev);
135 if (r)
136 return r;
137
138 return size;
139 }
140
display_rotate_show(struct omap_dss_device * dssdev,char * buf)141 static ssize_t display_rotate_show(struct omap_dss_device *dssdev, char *buf)
142 {
143 int rotate;
144 if (!dssdev->driver->get_rotate)
145 return -ENOENT;
146 rotate = dssdev->driver->get_rotate(dssdev);
147 return sysfs_emit(buf, "%u\n", rotate);
148 }
149
display_rotate_store(struct omap_dss_device * dssdev,const char * buf,size_t size)150 static ssize_t display_rotate_store(struct omap_dss_device *dssdev,
151 const char *buf, size_t size)
152 {
153 int rot, r;
154
155 if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
156 return -ENOENT;
157
158 r = kstrtoint(buf, 0, &rot);
159 if (r)
160 return r;
161
162 r = dssdev->driver->set_rotate(dssdev, rot);
163 if (r)
164 return r;
165
166 return size;
167 }
168
display_mirror_show(struct omap_dss_device * dssdev,char * buf)169 static ssize_t display_mirror_show(struct omap_dss_device *dssdev, char *buf)
170 {
171 int mirror;
172 if (!dssdev->driver->get_mirror)
173 return -ENOENT;
174 mirror = dssdev->driver->get_mirror(dssdev);
175 return sysfs_emit(buf, "%u\n", mirror);
176 }
177
display_mirror_store(struct omap_dss_device * dssdev,const char * buf,size_t size)178 static ssize_t display_mirror_store(struct omap_dss_device *dssdev,
179 const char *buf, size_t size)
180 {
181 int r;
182 bool mirror;
183
184 if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
185 return -ENOENT;
186
187 r = kstrtobool(buf, &mirror);
188 if (r)
189 return r;
190
191 r = dssdev->driver->set_mirror(dssdev, mirror);
192 if (r)
193 return r;
194
195 return size;
196 }
197
display_wss_show(struct omap_dss_device * dssdev,char * buf)198 static ssize_t display_wss_show(struct omap_dss_device *dssdev, char *buf)
199 {
200 unsigned int wss;
201
202 if (!dssdev->driver->get_wss)
203 return -ENOENT;
204
205 wss = dssdev->driver->get_wss(dssdev);
206
207 return sysfs_emit(buf, "0x%05x\n", wss);
208 }
209
display_wss_store(struct omap_dss_device * dssdev,const char * buf,size_t size)210 static ssize_t display_wss_store(struct omap_dss_device *dssdev,
211 const char *buf, size_t size)
212 {
213 u32 wss;
214 int r;
215
216 if (!dssdev->driver->get_wss || !dssdev->driver->set_wss)
217 return -ENOENT;
218
219 r = kstrtou32(buf, 0, &wss);
220 if (r)
221 return r;
222
223 if (wss > 0xfffff)
224 return -EINVAL;
225
226 r = dssdev->driver->set_wss(dssdev, wss);
227 if (r)
228 return r;
229
230 return size;
231 }
232
233 struct display_attribute {
234 struct attribute attr;
235 ssize_t (*show)(struct omap_dss_device *, char *);
236 ssize_t (*store)(struct omap_dss_device *, const char *, size_t);
237 };
238
239 #define DISPLAY_ATTR(_name, _mode, _show, _store) \
240 struct display_attribute display_attr_##_name = \
241 __ATTR(_name, _mode, _show, _store)
242
243 static DISPLAY_ATTR(name, S_IRUGO, display_name_show, NULL);
244 static DISPLAY_ATTR(display_name, S_IRUGO, display_name_show, NULL);
245 static DISPLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
246 display_enabled_show, display_enabled_store);
247 static DISPLAY_ATTR(tear_elim, S_IRUGO|S_IWUSR,
248 display_tear_show, display_tear_store);
249 static DISPLAY_ATTR(timings, S_IRUGO|S_IWUSR,
250 display_timings_show, display_timings_store);
251 static DISPLAY_ATTR(rotate, S_IRUGO|S_IWUSR,
252 display_rotate_show, display_rotate_store);
253 static DISPLAY_ATTR(mirror, S_IRUGO|S_IWUSR,
254 display_mirror_show, display_mirror_store);
255 static DISPLAY_ATTR(wss, S_IRUGO|S_IWUSR,
256 display_wss_show, display_wss_store);
257
258 static struct attribute *display_sysfs_attrs[] = {
259 &display_attr_name.attr,
260 &display_attr_display_name.attr,
261 &display_attr_enabled.attr,
262 &display_attr_tear_elim.attr,
263 &display_attr_timings.attr,
264 &display_attr_rotate.attr,
265 &display_attr_mirror.attr,
266 &display_attr_wss.attr,
267 NULL
268 };
269 ATTRIBUTE_GROUPS(display_sysfs);
270
display_attr_show(struct kobject * kobj,struct attribute * attr,char * buf)271 static ssize_t display_attr_show(struct kobject *kobj, struct attribute *attr,
272 char *buf)
273 {
274 struct omap_dss_device *dssdev;
275 struct display_attribute *display_attr;
276
277 dssdev = container_of(kobj, struct omap_dss_device, kobj);
278 display_attr = container_of(attr, struct display_attribute, attr);
279
280 if (!display_attr->show)
281 return -ENOENT;
282
283 return display_attr->show(dssdev, buf);
284 }
285
display_attr_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t size)286 static ssize_t display_attr_store(struct kobject *kobj, struct attribute *attr,
287 const char *buf, size_t size)
288 {
289 struct omap_dss_device *dssdev;
290 struct display_attribute *display_attr;
291
292 dssdev = container_of(kobj, struct omap_dss_device, kobj);
293 display_attr = container_of(attr, struct display_attribute, attr);
294
295 if (!display_attr->store)
296 return -ENOENT;
297
298 return display_attr->store(dssdev, buf, size);
299 }
300
301 static const struct sysfs_ops display_sysfs_ops = {
302 .show = display_attr_show,
303 .store = display_attr_store,
304 };
305
306 static struct kobj_type display_ktype = {
307 .sysfs_ops = &display_sysfs_ops,
308 .default_groups = display_sysfs_groups,
309 };
310
display_init_sysfs(struct platform_device * pdev)311 int display_init_sysfs(struct platform_device *pdev)
312 {
313 struct omap_dss_device *dssdev = NULL;
314 int r;
315
316 for_each_dss_dev(dssdev) {
317 r = kobject_init_and_add(&dssdev->kobj, &display_ktype,
318 &pdev->dev.kobj, "%s", dssdev->alias);
319 if (r) {
320 DSSERR("failed to create sysfs files\n");
321 omap_dss_put_device(dssdev);
322 goto err;
323 }
324 }
325
326 return 0;
327
328 err:
329 display_uninit_sysfs(pdev);
330
331 return r;
332 }
333
display_uninit_sysfs(struct platform_device * pdev)334 void display_uninit_sysfs(struct platform_device *pdev)
335 {
336 struct omap_dss_device *dssdev = NULL;
337
338 for_each_dss_dev(dssdev) {
339 if (kobject_name(&dssdev->kobj) == NULL)
340 continue;
341
342 kobject_del(&dssdev->kobj);
343 kobject_put(&dssdev->kobj);
344
345 memset(&dssdev->kobj, 0, sizeof(dssdev->kobj));
346 }
347 }
348