Skip to content

HDMI Interrupt-Based Hotplug for Devices with a GPIO #4327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: rpi-5.10.y
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 22 additions & 17 deletions drivers/gpu/drm/vc4/vc4_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -484,9 +484,8 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
WARN_ON(clk_prepare_enable(vc4_hdmi->hsm_clock));

if (vc4_hdmi->hpd_gpio) {
if (gpio_get_value_cansleep(vc4_hdmi->hpd_gpio) ^
vc4_hdmi->hpd_active_low)
connected = true;
if (gpiod_get_value_cansleep(vc4_hdmi->hpd_gpio))
connected = true;
} else if (drm_probe_ddc(vc4_hdmi->ddc)) {
connected = true;
} else if (HDMI_READ(HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED) {
Expand Down Expand Up @@ -2198,6 +2197,21 @@ static int vc4_hdmi_hotplug_init(struct vc4_hdmi *vc4_hdmi)
if (ret)
return ret;

connector->polled = DRM_CONNECTOR_POLL_HPD;
} else if (vc4_hdmi->hpd_gpio) {
int irq = gpiod_to_irq(vc4_hdmi->hpd_gpio);
if (irq < 0)
return 0;

ret = devm_request_threaded_irq(dev, irq,
NULL, vc4_hdmi_hpd_irq_thread,
IRQF_ONESHOT |
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
"vc4 hdmi hpd", vc4_hdmi);
if (ret)
return ret;

connector->polled = DRM_CONNECTOR_POLL_HPD;
}

Expand Down Expand Up @@ -2720,7 +2734,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
struct vc4_hdmi *vc4_hdmi;
struct drm_encoder *encoder;
struct device_node *ddc_node;
u32 value;
int ret;

vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL);
Expand Down Expand Up @@ -2759,18 +2772,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
/* Only use the GPIO HPD pin if present in the DT, otherwise
* we'll use the HDMI core's register.
*/
if (of_find_property(dev->of_node, "hpd-gpios", &value)) {
enum of_gpio_flags hpd_gpio_flags;

vc4_hdmi->hpd_gpio = of_get_named_gpio_flags(dev->of_node,
"hpd-gpios", 0,
&hpd_gpio_flags);
if (vc4_hdmi->hpd_gpio < 0) {
ret = vc4_hdmi->hpd_gpio;
goto err_unprepare_hsm;
}

vc4_hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
vc4_hdmi->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
if (IS_ERR(vc4_hdmi->hpd_gpio)) {
ret = PTR_ERR(vc4_hdmi->hpd_gpio);
goto err_put_ddc;
}

vc4_hdmi->disable_wifi_frequencies =
Expand Down Expand Up @@ -2828,8 +2833,8 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
err_destroy_encoder:
drm_encoder_cleanup(encoder);
err_unprepare_hsm:
pm_runtime_disable(dev);
err_put_ddc:
put_device(&vc4_hdmi->ddc->dev);

return ret;
Expand Down
3 changes: 1 addition & 2 deletions drivers/gpu/drm/vc4/vc4_hdmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,7 @@ struct vc4_hdmi {
/* VC5 Only */
void __iomem *rm_regs;

int hpd_gpio;
bool hpd_active_low;
struct gpio_desc *hpd_gpio;

/*
* On some systems (like the RPi4), some modes are in the same
Expand Down
23 changes: 23 additions & 0 deletions drivers/pinctrl/bcm/pinctrl-bcm2835.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,28 @@ static int bcm2835_gpio_direction_output(struct gpio_chip *chip,
return pinctrl_gpio_direction_output(chip->base + offset);
}

static int bcm2835_gpio_irq_map(struct irq_domain *d,
unsigned int irq,
irq_hw_number_t hwirq)
{
unsigned bank = GPIO_REG_OFFSET(hwirq);

/*
* FIXME: It appears that the interrupts in any bank but the
* first are not working.
*/
if (bank)
return -EINVAL;

return gpiochip_irq_map(d, irq, hwirq);
}

static const struct irq_domain_ops bcm2835_irq_domain_ops = {
.map = bcm2835_gpio_irq_map,
.unmap = gpiochip_irq_unmap,
.xlate = irq_domain_xlate_twocell,
};

static const struct gpio_chip bcm2835_gpio_chip = {
.label = MODULE_NAME,
.owner = THIS_MODULE,
Expand Down Expand Up @@ -1258,6 +1280,7 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)

girq = &pc->gpio_chip.irq;
girq->chip = &bcm2835_gpio_irq_chip;
girq->domain_ops = &bcm2835_irq_domain_ops;
girq->parent_handler = bcm2835_gpio_irq_handler;
girq->num_parents = BCM2835_NUM_IRQS;
girq->parents = devm_kcalloc(dev, BCM2835_NUM_IRQS,
Expand Down