From 31ecc0af39df50e2ae68f8ae267a0d8ccaec6249 Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Thu, 10 Oct 2024 03:00:49 -0500 Subject: [PATCH] drm/tegra: Create switch class for primary hdmi Change-Id: I353b1c5ad98bcd69b4827b0c74327511b4d8222d --- drivers/gpu/drm/tegra/drm.h | 13 ++++ drivers/gpu/drm/tegra/output.c | 137 ++++++++++++++++++++++++++++++++- 2 files changed, 148 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 4792158193ec..2bad90c00e7a 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -126,9 +126,22 @@ void tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt, struct cec_notifier; +struct notify_dev { + const char *name; + struct device *dev; + int index; + int state; + + ssize_t (*print_name)(struct notify_dev *sdev, char *buf); + ssize_t (*print_state)(struct notify_dev *sdev, char *buf); +}; + struct tegra_output { struct device_node *of_node; struct device *dev; + struct notify_dev notify_data; + struct class *switch_class; + atomic_t switch_count; struct drm_bridge *bridge; struct drm_panel *panel; diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 49e4f63a5550..d10eea3af37a 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -18,6 +18,117 @@ #include +static ssize_t state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct notify_dev *sdev = (struct notify_dev *) + dev_get_drvdata(dev); + + if (sdev->print_state) { + ret = sdev->print_state(sdev, buf); + if (ret >= 0) + return ret; + } + return sprintf(buf, "%d\n", sdev->state); +} + +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret; + struct notify_dev *sdev = (struct notify_dev *) + dev_get_drvdata(dev); + + if (sdev->print_name) { + ret = sdev->print_name(sdev, buf); + if (ret >= 0) + return ret; + } + return sprintf(buf, "%s\n", sdev->name); +} + +static DEVICE_ATTR_RO(state); +static DEVICE_ATTR_RO(name); + +static int create_switch_class(struct class **switch_class, atomic_t *switch_count) +{ + if (!*switch_class) { + *switch_class = class_create("switch"); + if (IS_ERR(*switch_class)) + return PTR_ERR(*switch_class); + atomic_set(switch_count, 0); + } + + return 0; +} + +static int disp_uevent_dev_register(struct notify_dev *sdev, struct class **switch_class, atomic_t *switch_count) +{ + int ret; + + if (!*switch_class) { + ret = create_switch_class(switch_class, switch_count); + if (ret != 0) + return ret; + } + + sdev->index = atomic_inc_return(switch_count); + sdev->dev = device_create(*switch_class, NULL, + MKDEV(0, sdev->index), NULL, sdev->name); + if (IS_ERR(sdev->dev)) + return PTR_ERR(sdev->dev); + + ret = device_create_file(sdev->dev, &dev_attr_state); + if (ret < 0) + goto err_create_file_1; + ret = device_create_file(sdev->dev, &dev_attr_name); + if (ret < 0) + goto err_create_file_2; + + dev_set_drvdata(sdev->dev, sdev); + sdev->state = 0; + return 0; + +err_create_file_2: + device_remove_file(sdev->dev, &dev_attr_state); +err_create_file_1: + device_destroy(*switch_class, MKDEV(0, sdev->index)); + printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name); + return ret; +} + +static void disp_uevent_dev_unregister(struct notify_dev *sdev, struct class *switch_class) +{ + device_remove_file(sdev->dev, &dev_attr_name); + device_remove_file(sdev->dev, &dev_attr_state); + device_destroy(switch_class, MKDEV(0, sdev->index)); + dev_set_drvdata(sdev->dev, NULL); +} + +static int notify_uevent_user(struct notify_dev *sdev, int state) +{ + char *envp[3]; + char name_buf[120]; + char state_buf[120]; + + if (sdev == NULL) + return -1; + + if (sdev->state != state) + sdev->state = state; + + snprintf(name_buf, sizeof(name_buf), "SWITCH_NAME=%s", sdev->name); + envp[0] = name_buf; + snprintf(state_buf, sizeof(state_buf), "SWITCH_STATE=%d", sdev->state); + envp[1] = state_buf; + envp[2] = NULL; + + kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp); + + return 0; +} + int tegra_output_connector_get_modes(struct drm_connector *connector) { struct tegra_output *output = connector_to_output(connector); @@ -88,9 +199,14 @@ static irqreturn_t hpd_irq(int irq, void *data) { struct tegra_output *output = data; - if (output->connector.dev) + if (output->connector.dev) { drm_helper_hpd_irq_event(output->connector.dev); + if (output->connector.connector_type == DRM_MODE_CONNECTOR_HDMIA || + output->connector.connector_type == DRM_MODE_CONNECTOR_HDMIB) + notify_uevent_user(&output->notify_data, (output->connector.status == connector_status_connected ? 1 : 0)); + } + return IRQ_HANDLED; } @@ -207,6 +323,7 @@ void tegra_output_remove(struct tegra_output *output) int tegra_output_init(struct drm_device *drm, struct tegra_output *output) { int connector_type; + int err; /* * The connector is now registered and ready to receive hotplug events @@ -228,6 +345,20 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output) &conn_info); if (!output->cec) return -ENOMEM; + + if (output->hpd_gpio) { + output->notify_data.name = "hdmi"; // now hwc not support DP + output->notify_data.index = 0; + output->notify_data.state = 0; + err = disp_uevent_dev_register(&output->notify_data, &output->switch_class, &output->switch_count); + if (err < 0) { + dev_err(output->dev, "switch_dev_register failed: %d\n", err); + return err; + } + + if (tegra_output_connector_detect(&output->connector, false) == connector_status_connected) + notify_uevent_user(&output->notify_data, 1); + } } return 0; @@ -239,8 +370,10 @@ void tegra_output_exit(struct tegra_output *output) * The connector is going away, so the interrupt must be disabled to * prevent the hotplug interrupt handler from potentially crashing. */ - if (output->hpd_gpio) + if (output->hpd_gpio) { disable_irq(output->hpd_irq); + disp_uevent_dev_unregister(&output->notify_data, output->switch_class); + } } void tegra_output_find_possible_crtcs(struct tegra_output *output,