linux/drivers/gpu/drm/tegra/output.c
Dave Airlie 877fa9a42d drm/tegra: Changes for v4.8-rc1
This set of changes contains a bunch of cleanups to the host1x driver as
 well as the addition of a pin controller for DPAUX, which is required by
 boards to configure the DPAUX pads in AUX mode (for DisplayPort) or I2C
 mode (for HDMI and DDC).
 
 Included is also a bit of rework of the SOR driver in preparation to add
 DisplayPort support as well as some refactoring and cleanup.
 
 Finally, all output drivers are converted to runtime PM, which greatly
 simplifies the handling of clocks and resets.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJXh5n1AAoJEN0jrNd/PrOh+KcP/0SLO9UKvGBsfD0EFvkxx/NV
 VZvqCWP0ARCGDAZn890O4sqwgHdRDF+hp0HcUt8wDt4o3VTYJIdifr/5tunOZ6+w
 1IatWrjJjpyvZs9bveEvtJ8Hbkq5h0esA1StcqheRUiE0o+uQppdWO1NLO+HxssQ
 f4qV4GQ7trJQdjr9OGfGJigsvOjwGVbPL7CRrKKLmbfTiqy9zhi5bOyUfojtMEL6
 BvH9RMqC0ke70sGtcPgigbL7wgnbkH0uR3sV5TwU5dErrvh0tnhYU1a1ednPotJW
 z56+W8hWfC3XLIiU9mju1l+3sQA0BuS9ZQ+6VhdRTKxv85CUI5dUKT0/h5BtRrK9
 0XgPwrn4YpAr17oFEGdqFPhefkDkbb/1WrWmgUEi8/YaimdCXNkf8rILZvWUVQdt
 q9GbbmEiGNR2FvBEN7s/nT96E5XEzfLTjOzTOP0c5RF9cOK+Ul9t0t44eBn4Wqbk
 x6rTv37eieK5RVYd24xKKnzf5uSMpU+RTea4Vpr0xdH6glGcrTJ5ogub0iIsY0Jn
 A9M1pKadQvu//95ZzGWEyrqAr66cDG8vGjZn9z24PRaNZEbzvY3ohj2/qOSQoRFY
 HPmswshI0DnyFPWmdhhlPy3di0zNDP6kfPo6NhotoAl2Uwd+RrY/VyWjDG0dwV0R
 nSkcnGES5hnf3gguXEmO
 =7xlG
 -----END PGP SIGNATURE-----

Merge tag 'drm/tegra/for-4.8-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next

drm/tegra: Changes for v4.8-rc1

This set of changes contains a bunch of cleanups to the host1x driver as
well as the addition of a pin controller for DPAUX, which is required by
boards to configure the DPAUX pads in AUX mode (for DisplayPort) or I2C
mode (for HDMI and DDC).

Included is also a bit of rework of the SOR driver in preparation to add
DisplayPort support as well as some refactoring and cleanup.

Finally, all output drivers are converted to runtime PM, which greatly
simplifies the handling of clocks and resets.

* tag 'drm/tegra/for-4.8-rc1' of git://anongit.freedesktop.org/tegra/linux: (35 commits)
  drm/tegra: sor: Reject HDMI 2.0 modes
  drm/tegra: sor: Prepare for generic PM domain support
  drm/tegra: dsi: Prepare for generic PM domain support
  drm/tegra: sor: Make XBAR configurable per SoC
  drm/tegra: sor: Use sor1_src clock to set parent for HDMI
  dt-bindings: display: tegra: Add source clock for SOR
  drm/tegra: sor: Implement sor1_brick clock
  drm/tegra: sor: Implement runtime PM
  drm/tegra: hdmi: Implement runtime PM
  drm/tegra: dsi: Implement runtime PM
  drm/tegra: dc: Implement runtime PM
  drm/tegra: hdmi: Enable audio over HDMI
  drm/tegra: sor: Do not support deep color modes
  drm/tegra: sor: Extract tegra_sor_mode_set()
  drm/tegra: sor: Split out tegra_sor_apply_config()
  drm/tegra: sor: Rename tegra_sor_calc_config()
  drm/tegra: sor: Factor out tegra_sor_set_parent_clock()
  drm/tegra: dpaux: Add pinctrl support
  dt-bindings: Add bindings for Tegra DPAUX pinctrl driver
  drm/tegra: Prepare DPAUX for supporting generic PM domains
  ...
2016-07-16 11:23:50 +10:00

216 lines
5.0 KiB
C

/*
* Copyright (C) 2012 Avionic Design GmbH
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <drm/drm_atomic_helper.h>
#include <drm/drm_panel.h>
#include "drm.h"
int tegra_output_connector_get_modes(struct drm_connector *connector)
{
struct tegra_output *output = connector_to_output(connector);
struct edid *edid = NULL;
int err = 0;
/*
* If the panel provides one or more modes, use them exclusively and
* ignore any other means of obtaining a mode.
*/
if (output->panel) {
err = output->panel->funcs->get_modes(output->panel);
if (err > 0)
return err;
}
if (output->edid)
edid = kmemdup(output->edid, sizeof(*edid), GFP_KERNEL);
else if (output->ddc)
edid = drm_get_edid(connector, output->ddc);
drm_mode_connector_update_edid_property(connector, edid);
if (edid) {
err = drm_add_edid_modes(connector, edid);
drm_edid_to_eld(connector, edid);
kfree(edid);
}
return err;
}
enum drm_connector_status
tegra_output_connector_detect(struct drm_connector *connector, bool force)
{
struct tegra_output *output = connector_to_output(connector);
enum drm_connector_status status = connector_status_unknown;
if (gpio_is_valid(output->hpd_gpio)) {
if (output->hpd_gpio_flags & OF_GPIO_ACTIVE_LOW) {
if (gpio_get_value(output->hpd_gpio) != 0)
status = connector_status_disconnected;
else
status = connector_status_connected;
} else {
if (gpio_get_value(output->hpd_gpio) == 0)
status = connector_status_disconnected;
else
status = connector_status_connected;
}
} else {
if (!output->panel)
status = connector_status_disconnected;
else
status = connector_status_connected;
}
return status;
}
void tegra_output_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
void tegra_output_encoder_destroy(struct drm_encoder *encoder)
{
drm_encoder_cleanup(encoder);
}
static irqreturn_t hpd_irq(int irq, void *data)
{
struct tegra_output *output = data;
if (output->connector.dev)
drm_helper_hpd_irq_event(output->connector.dev);
return IRQ_HANDLED;
}
int tegra_output_probe(struct tegra_output *output)
{
struct device_node *ddc, *panel;
int err, size;
if (!output->of_node)
output->of_node = output->dev->of_node;
panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
if (panel) {
output->panel = of_drm_find_panel(panel);
if (!output->panel)
return -EPROBE_DEFER;
of_node_put(panel);
}
output->edid = of_get_property(output->of_node, "nvidia,edid", &size);
ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
if (ddc) {
output->ddc = of_find_i2c_adapter_by_node(ddc);
if (!output->ddc) {
err = -EPROBE_DEFER;
of_node_put(ddc);
return err;
}
of_node_put(ddc);
}
output->hpd_gpio = of_get_named_gpio_flags(output->of_node,
"nvidia,hpd-gpio", 0,
&output->hpd_gpio_flags);
if (gpio_is_valid(output->hpd_gpio)) {
unsigned long flags;
err = gpio_request_one(output->hpd_gpio, GPIOF_DIR_IN,
"HDMI hotplug detect");
if (err < 0) {
dev_err(output->dev, "gpio_request_one(): %d\n", err);
return err;
}
err = gpio_to_irq(output->hpd_gpio);
if (err < 0) {
dev_err(output->dev, "gpio_to_irq(): %d\n", err);
gpio_free(output->hpd_gpio);
return err;
}
output->hpd_irq = err;
flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
IRQF_ONESHOT;
err = request_threaded_irq(output->hpd_irq, NULL, hpd_irq,
flags, "hpd", output);
if (err < 0) {
dev_err(output->dev, "failed to request IRQ#%u: %d\n",
output->hpd_irq, err);
gpio_free(output->hpd_gpio);
return err;
}
output->connector.polled = DRM_CONNECTOR_POLL_HPD;
/*
* Disable the interrupt until the connector has been
* initialized to avoid a race in the hotplug interrupt
* handler.
*/
disable_irq(output->hpd_irq);
}
return 0;
}
void tegra_output_remove(struct tegra_output *output)
{
if (gpio_is_valid(output->hpd_gpio)) {
free_irq(output->hpd_irq, output);
gpio_free(output->hpd_gpio);
}
if (output->ddc)
put_device(&output->ddc->dev);
}
int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
{
int err;
if (output->panel) {
err = drm_panel_attach(output->panel, &output->connector);
if (err < 0)
return err;
}
/*
* The connector is now registered and ready to receive hotplug events
* so the hotplug interrupt can be enabled.
*/
if (gpio_is_valid(output->hpd_gpio))
enable_irq(output->hpd_irq);
return 0;
}
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 (gpio_is_valid(output->hpd_gpio))
disable_irq(output->hpd_irq);
if (output->panel)
drm_panel_detach(output->panel);
}