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