| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (C) 2009 Nokia Corporation |
| * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| * |
| * Some code and ideas taken from drivers/video/omap/ driver |
| * by Imre Deak. |
| */ |
| |
| #define DSS_SUBSYS_NAME "MANAGER" |
| |
| #include <linux/kernel.h> |
| #include <linux/kstrtox.h> |
| #include <linux/slab.h> |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <linux/jiffies.h> |
| |
| #include <video/omapfb_dss.h> |
| |
| #include "dss.h" |
| #include "dss_features.h" |
| |
| static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf) |
| { |
| return sysfs_emit(buf, "%s\n", mgr->name); |
| } |
| |
| static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf) |
| { |
| struct omap_dss_device *dssdev = mgr->get_device(mgr); |
| |
| return sysfs_emit(buf, "%s\n", dssdev ? |
| dssdev->name : "<none>"); |
| } |
| |
| static int manager_display_match(struct omap_dss_device *dssdev, void *data) |
| { |
| const char *str = data; |
| |
| return sysfs_streq(dssdev->name, str); |
| } |
| |
| static ssize_t manager_display_store(struct omap_overlay_manager *mgr, |
| const char *buf, size_t size) |
| { |
| int r = 0; |
| size_t len = size; |
| struct omap_dss_device *dssdev = NULL; |
| struct omap_dss_device *old_dssdev; |
| |
| if (buf[size-1] == '\n') |
| --len; |
| |
| if (len > 0) |
| dssdev = omap_dss_find_device((void *)buf, |
| manager_display_match); |
| |
| if (len > 0 && dssdev == NULL) |
| return -EINVAL; |
| |
| if (dssdev) { |
| DSSDBG("display %s found\n", dssdev->name); |
| |
| if (omapdss_device_is_connected(dssdev)) { |
| DSSERR("new display is already connected\n"); |
| r = -EINVAL; |
| goto put_device; |
| } |
| |
| if (omapdss_device_is_enabled(dssdev)) { |
| DSSERR("new display is not disabled\n"); |
| r = -EINVAL; |
| goto put_device; |
| } |
| } |
| |
| old_dssdev = mgr->get_device(mgr); |
| if (old_dssdev) { |
| if (omapdss_device_is_enabled(old_dssdev)) { |
| DSSERR("old display is not disabled\n"); |
| r = -EINVAL; |
| goto put_device; |
| } |
| |
| old_dssdev->driver->disconnect(old_dssdev); |
| } |
| |
| if (dssdev) { |
| r = dssdev->driver->connect(dssdev); |
| if (r) { |
| DSSERR("failed to connect new device\n"); |
| goto put_device; |
| } |
| |
| old_dssdev = mgr->get_device(mgr); |
| if (old_dssdev != dssdev) { |
| DSSERR("failed to connect device to this manager\n"); |
| dssdev->driver->disconnect(dssdev); |
| goto put_device; |
| } |
| |
| r = mgr->apply(mgr); |
| if (r) { |
| DSSERR("failed to apply dispc config\n"); |
| goto put_device; |
| } |
| } |
| |
| put_device: |
| if (dssdev) |
| omap_dss_put_device(dssdev); |
| |
| return r ? r : size; |
| } |
| |
| static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr, |
| char *buf) |
| { |
| struct omap_overlay_manager_info info; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| return sysfs_emit(buf, "%#x\n", info.default_color); |
| } |
| |
| static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, |
| const char *buf, size_t size) |
| { |
| struct omap_overlay_manager_info info; |
| u32 color; |
| int r; |
| |
| r = kstrtouint(buf, 0, &color); |
| if (r) |
| return r; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| info.default_color = color; |
| |
| r = mgr->set_manager_info(mgr, &info); |
| if (r) |
| return r; |
| |
| r = mgr->apply(mgr); |
| if (r) |
| return r; |
| |
| return size; |
| } |
| |
| static const char *trans_key_type_str[] = { |
| "gfx-destination", |
| "video-source", |
| }; |
| |
| static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr, |
| char *buf) |
| { |
| enum omap_dss_trans_key_type key_type; |
| struct omap_overlay_manager_info info; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| key_type = info.trans_key_type; |
| BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str)); |
| |
| return sysfs_emit(buf, "%s\n", trans_key_type_str[key_type]); |
| } |
| |
| static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr, |
| const char *buf, size_t size) |
| { |
| struct omap_overlay_manager_info info; |
| int r; |
| |
| r = sysfs_match_string(trans_key_type_str, buf); |
| if (r < 0) |
| return r; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| info.trans_key_type = r; |
| |
| r = mgr->set_manager_info(mgr, &info); |
| if (r) |
| return r; |
| |
| r = mgr->apply(mgr); |
| if (r) |
| return r; |
| |
| return size; |
| } |
| |
| static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr, |
| char *buf) |
| { |
| struct omap_overlay_manager_info info; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| return sysfs_emit(buf, "%#x\n", info.trans_key); |
| } |
| |
| static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, |
| const char *buf, size_t size) |
| { |
| struct omap_overlay_manager_info info; |
| u32 key_value; |
| int r; |
| |
| r = kstrtouint(buf, 0, &key_value); |
| if (r) |
| return r; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| info.trans_key = key_value; |
| |
| r = mgr->set_manager_info(mgr, &info); |
| if (r) |
| return r; |
| |
| r = mgr->apply(mgr); |
| if (r) |
| return r; |
| |
| return size; |
| } |
| |
| static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr, |
| char *buf) |
| { |
| struct omap_overlay_manager_info info; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| return sysfs_emit(buf, "%d\n", info.trans_enabled); |
| } |
| |
| static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, |
| const char *buf, size_t size) |
| { |
| struct omap_overlay_manager_info info; |
| bool enable; |
| int r; |
| |
| r = kstrtobool(buf, &enable); |
| if (r) |
| return r; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| info.trans_enabled = enable; |
| |
| r = mgr->set_manager_info(mgr, &info); |
| if (r) |
| return r; |
| |
| r = mgr->apply(mgr); |
| if (r) |
| return r; |
| |
| return size; |
| } |
| |
| static ssize_t manager_alpha_blending_enabled_show( |
| struct omap_overlay_manager *mgr, char *buf) |
| { |
| struct omap_overlay_manager_info info; |
| |
| if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) |
| return -ENODEV; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| return sysfs_emit(buf, "%d\n", |
| info.partial_alpha_enabled); |
| } |
| |
| static ssize_t manager_alpha_blending_enabled_store( |
| struct omap_overlay_manager *mgr, |
| const char *buf, size_t size) |
| { |
| struct omap_overlay_manager_info info; |
| bool enable; |
| int r; |
| |
| if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) |
| return -ENODEV; |
| |
| r = kstrtobool(buf, &enable); |
| if (r) |
| return r; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| info.partial_alpha_enabled = enable; |
| |
| r = mgr->set_manager_info(mgr, &info); |
| if (r) |
| return r; |
| |
| r = mgr->apply(mgr); |
| if (r) |
| return r; |
| |
| return size; |
| } |
| |
| static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr, |
| char *buf) |
| { |
| struct omap_overlay_manager_info info; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| return sysfs_emit(buf, "%d\n", info.cpr_enable); |
| } |
| |
| static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr, |
| const char *buf, size_t size) |
| { |
| struct omap_overlay_manager_info info; |
| int r; |
| bool enable; |
| |
| if (!dss_has_feature(FEAT_CPR)) |
| return -ENODEV; |
| |
| r = kstrtobool(buf, &enable); |
| if (r) |
| return r; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| if (info.cpr_enable == enable) |
| return size; |
| |
| info.cpr_enable = enable; |
| |
| r = mgr->set_manager_info(mgr, &info); |
| if (r) |
| return r; |
| |
| r = mgr->apply(mgr); |
| if (r) |
| return r; |
| |
| return size; |
| } |
| |
| static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr, |
| char *buf) |
| { |
| struct omap_overlay_manager_info info; |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| return sysfs_emit(buf, |
| "%d %d %d %d %d %d %d %d %d\n", |
| info.cpr_coefs.rr, |
| info.cpr_coefs.rg, |
| info.cpr_coefs.rb, |
| info.cpr_coefs.gr, |
| info.cpr_coefs.gg, |
| info.cpr_coefs.gb, |
| info.cpr_coefs.br, |
| info.cpr_coefs.bg, |
| info.cpr_coefs.bb); |
| } |
| |
| static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr, |
| const char *buf, size_t size) |
| { |
| struct omap_overlay_manager_info info; |
| struct omap_dss_cpr_coefs coefs; |
| int r, i; |
| s16 *arr; |
| |
| if (!dss_has_feature(FEAT_CPR)) |
| return -ENODEV; |
| |
| if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd", |
| &coefs.rr, &coefs.rg, &coefs.rb, |
| &coefs.gr, &coefs.gg, &coefs.gb, |
| &coefs.br, &coefs.bg, &coefs.bb) != 9) |
| return -EINVAL; |
| |
| arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb, |
| coefs.gr, coefs.gg, coefs.gb, |
| coefs.br, coefs.bg, coefs.bb }; |
| |
| for (i = 0; i < 9; ++i) { |
| if (arr[i] < -512 || arr[i] > 511) |
| return -EINVAL; |
| } |
| |
| mgr->get_manager_info(mgr, &info); |
| |
| info.cpr_coefs = coefs; |
| |
| r = mgr->set_manager_info(mgr, &info); |
| if (r) |
| return r; |
| |
| r = mgr->apply(mgr); |
| if (r) |
| return r; |
| |
| return size; |
| } |
| |
| struct manager_attribute { |
| struct attribute attr; |
| ssize_t (*show)(struct omap_overlay_manager *, char *); |
| ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t); |
| }; |
| |
| #define MANAGER_ATTR(_name, _mode, _show, _store) \ |
| struct manager_attribute manager_attr_##_name = \ |
| __ATTR(_name, _mode, _show, _store) |
| |
| static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL); |
| static MANAGER_ATTR(display, S_IRUGO|S_IWUSR, |
| manager_display_show, manager_display_store); |
| static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR, |
| manager_default_color_show, manager_default_color_store); |
| static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR, |
| manager_trans_key_type_show, manager_trans_key_type_store); |
| static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR, |
| manager_trans_key_value_show, manager_trans_key_value_store); |
| static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR, |
| manager_trans_key_enabled_show, |
| manager_trans_key_enabled_store); |
| static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR, |
| manager_alpha_blending_enabled_show, |
| manager_alpha_blending_enabled_store); |
| static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR, |
| manager_cpr_enable_show, |
| manager_cpr_enable_store); |
| static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR, |
| manager_cpr_coef_show, |
| manager_cpr_coef_store); |
| |
| |
| static struct attribute *manager_sysfs_attrs[] = { |
| &manager_attr_name.attr, |
| &manager_attr_display.attr, |
| &manager_attr_default_color.attr, |
| &manager_attr_trans_key_type.attr, |
| &manager_attr_trans_key_value.attr, |
| &manager_attr_trans_key_enabled.attr, |
| &manager_attr_alpha_blending_enabled.attr, |
| &manager_attr_cpr_enable.attr, |
| &manager_attr_cpr_coef.attr, |
| NULL |
| }; |
| ATTRIBUTE_GROUPS(manager_sysfs); |
| |
| static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr, |
| char *buf) |
| { |
| struct omap_overlay_manager *manager; |
| struct manager_attribute *manager_attr; |
| |
| manager = container_of(kobj, struct omap_overlay_manager, kobj); |
| manager_attr = container_of(attr, struct manager_attribute, attr); |
| |
| if (!manager_attr->show) |
| return -ENOENT; |
| |
| return manager_attr->show(manager, buf); |
| } |
| |
| static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr, |
| const char *buf, size_t size) |
| { |
| struct omap_overlay_manager *manager; |
| struct manager_attribute *manager_attr; |
| |
| manager = container_of(kobj, struct omap_overlay_manager, kobj); |
| manager_attr = container_of(attr, struct manager_attribute, attr); |
| |
| if (!manager_attr->store) |
| return -ENOENT; |
| |
| return manager_attr->store(manager, buf, size); |
| } |
| |
| static const struct sysfs_ops manager_sysfs_ops = { |
| .show = manager_attr_show, |
| .store = manager_attr_store, |
| }; |
| |
| static struct kobj_type manager_ktype = { |
| .sysfs_ops = &manager_sysfs_ops, |
| .default_groups = manager_sysfs_groups, |
| }; |
| |
| int dss_manager_kobj_init(struct omap_overlay_manager *mgr, |
| struct platform_device *pdev) |
| { |
| return kobject_init_and_add(&mgr->kobj, &manager_ktype, |
| &pdev->dev.kobj, "manager%d", mgr->id); |
| } |
| |
| void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr) |
| { |
| kobject_del(&mgr->kobj); |
| kobject_put(&mgr->kobj); |
| |
| memset(&mgr->kobj, 0, sizeof(mgr->kobj)); |
| } |