348 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2009 Nokia Corporation4 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>5 *6 * Some code and ideas taken from drivers/video/omap/ driver7 * 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 21static 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 28static 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 34static 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 61static 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 68static 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 88static 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 103static 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_VENC114 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#endif122 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 141static 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 150static 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 169static 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 178static 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 198static 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 210static 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 233struct 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 243static DISPLAY_ATTR(name, S_IRUGO, display_name_show, NULL);244static DISPLAY_ATTR(display_name, S_IRUGO, display_name_show, NULL);245static DISPLAY_ATTR(enabled, S_IRUGO|S_IWUSR,246 display_enabled_show, display_enabled_store);247static DISPLAY_ATTR(tear_elim, S_IRUGO|S_IWUSR,248 display_tear_show, display_tear_store);249static DISPLAY_ATTR(timings, S_IRUGO|S_IWUSR,250 display_timings_show, display_timings_store);251static DISPLAY_ATTR(rotate, S_IRUGO|S_IWUSR,252 display_rotate_show, display_rotate_store);253static DISPLAY_ATTR(mirror, S_IRUGO|S_IWUSR,254 display_mirror_show, display_mirror_store);255static DISPLAY_ATTR(wss, S_IRUGO|S_IWUSR,256 display_wss_show, display_wss_store);257 258static 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 NULL268};269ATTRIBUTE_GROUPS(display_sysfs);270 271static 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 286static 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 301static const struct sysfs_ops display_sysfs_ops = {302 .show = display_attr_show,303 .store = display_attr_store,304};305 306static struct kobj_type display_ktype = {307 .sysfs_ops = &display_sysfs_ops,308 .default_groups = display_sysfs_groups,309};310 311int 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 328err:329 display_uninit_sysfs(pdev);330 331 return r;332}333 334void 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