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