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