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 31static 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 38static 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 44static 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 71static 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 78static 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 98static 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 113static 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_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 151static 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 160static 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 179static 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 188static 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 208static 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 220static 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 243struct 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 253static DISPLAY_ATTR(name, S_IRUGO, display_name_show, NULL); 254static DISPLAY_ATTR(display_name, S_IRUGO, display_name_show, NULL); 255static DISPLAY_ATTR(enabled, S_IRUGO|S_IWUSR, 256 display_enabled_show, display_enabled_store); 257static DISPLAY_ATTR(tear_elim, S_IRUGO|S_IWUSR, 258 display_tear_show, display_tear_store); 259static DISPLAY_ATTR(timings, S_IRUGO|S_IWUSR, 260 display_timings_show, display_timings_store); 261static DISPLAY_ATTR(rotate, S_IRUGO|S_IWUSR, 262 display_rotate_show, display_rotate_store); 263static DISPLAY_ATTR(mirror, S_IRUGO|S_IWUSR, 264 display_mirror_show, display_mirror_store); 265static DISPLAY_ATTR(wss, S_IRUGO|S_IWUSR, 266 display_wss_show, display_wss_store); 267 268static 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 280static 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 295static 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 310static const struct sysfs_ops display_sysfs_ops = { 311 .show = display_attr_show, 312 .store = display_attr_store, 313}; 314 315static struct kobj_type display_ktype = { 316 .sysfs_ops = &display_sysfs_ops, 317 .default_attrs = display_sysfs_attrs, 318}; 319 320int 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, 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 337err: 338 display_uninit_sysfs(pdev); 339 340 return r; 341} 342 343void 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