Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 877fa9a4 authored by Dave Airlie's avatar Dave Airlie
Browse files

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
  ...
parents e2b80bac 64ea25c3
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -208,6 +208,7 @@ of the following host1x client modules:
    See ../clocks/clock-bindings.txt for details.
  - clock-names: Must include the following entries:
    - sor: clock input for the SOR hardware
    - source: source clock for the SOR clock
    - parent: input for the pixel clock
    - dp: reference clock for the SOR clock
    - safe: safe reference for the SOR clock during power up
@@ -226,9 +227,9 @@ of the following host1x client modules:
  - nvidia,dpaux: phandle to a DispayPort AUX interface

- dpaux: DisplayPort AUX interface
  - compatible: For Tegra124, must contain "nvidia,tegra124-dpaux".  Otherwise,
    must contain '"nvidia,<chip>-dpaux", "nvidia,tegra124-dpaux"', where
    <chip> is tegra132.
  - compatible : Should contain one of the following:
    - "nvidia,tegra124-dpaux": for Tegra124 and Tegra132
    - "nvidia,tegra210-dpaux": for Tegra210
  - reg: Physical base address and length of the controller's registers.
  - interrupts: The interrupt outputs from the controller.
  - clocks: Must contain an entry for each entry in clock-names.
@@ -241,6 +242,12 @@ of the following host1x client modules:
  - reset-names: Must include the following entries:
    - dpaux
  - vdd-supply: phandle of a supply that powers the DisplayPort link
  - i2c-bus: Subnode where I2C slave devices are listed. This subnode
    must be always present. If there are no I2C slave devices, an empty
    node should be added. See ../../i2c/i2c.txt for more information.

  See ../pinctrl/nvidia,tegra124-dpaux-padctl.txt for information
  regarding the DPAUX pad controller bindings.

Example:

+60 −0
Original line number Diff line number Diff line
Device tree binding for NVIDIA Tegra DPAUX pad controller
========================================================

The Tegra Display Port Auxiliary (DPAUX) pad controller manages two pins
which can be assigned to either the DPAUX channel or to an I2C
controller.

This document defines the device-specific binding for the DPAUX pad
controller. Refer to pinctrl-bindings.txt in this directory for generic
information about pin controller device tree bindings. Please refer to
the binding document ../display/tegra/nvidia,tegra20-host1x.txt for more
details on the DPAUX binding.

Pin muxing:
-----------

Child nodes contain the pinmux configurations following the conventions
from the pinctrl-bindings.txt document.

Since only three configurations are possible, only three child nodes are
needed to describe the pin mux'ing options for the DPAUX pads.
Furthermore, given that the pad functions are only applicable to a
single set of pads, the child nodes only need to describe the pad group
the functions are being applied to rather than the individual pads.

Required properties:
- groups: Must be "dpaux-io"
- function: Must be either "aux", "i2c" or "off".

Example:
--------

	dpaux@545c0000 {
		...

		state_dpaux_aux: pinmux-aux {
			groups = "dpaux-io";
			function = "aux";
		};

		state_dpaux_i2c: pinmux-i2c {
			groups = "dpaux-io";
			function = "i2c";
		};

		state_dpaux_off: pinmux-off {
			groups = "dpaux-io";
			function = "off";
		};
	};

	...

	i2c@7000d100 {
		...
		pinctrl-0 = <&state_dpaux_i2c>;
		pinctrl-1 = <&state_dpaux_off>;
		pinctrl-names = "default", "idle";
		status = "disabled";
	};
+106 −70
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/iommu.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>

#include <soc/tegra/pmc.h>
@@ -1216,6 +1217,8 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)

	tegra_dc_stats_reset(&dc->stats);
	drm_crtc_vblank_off(crtc);

	pm_runtime_put_sync(dc->dev);
}

static void tegra_crtc_enable(struct drm_crtc *crtc)
@@ -1225,6 +1228,48 @@ static void tegra_crtc_enable(struct drm_crtc *crtc)
	struct tegra_dc *dc = to_tegra_dc(crtc);
	u32 value;

	pm_runtime_get_sync(dc->dev);

	/* initialize display controller */
	if (dc->syncpt) {
		u32 syncpt = host1x_syncpt_id(dc->syncpt);

		value = SYNCPT_CNTRL_NO_STALL;
		tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);

		value = SYNCPT_VSYNC_ENABLE | syncpt;
		tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
	}

	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
	tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);

	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
	tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);

	/* initialize timer */
	value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
		WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
	tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);

	value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
		WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
	tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);

	value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
	tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);

	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);

	if (dc->soc->supports_border_color)
		tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);

	/* apply PLL and pixel clock changes */
	tegra_dc_commit_state(dc, state);

	/* program display mode */
@@ -1685,7 +1730,6 @@ static int tegra_dc_init(struct host1x_client *client)
	struct tegra_drm *tegra = drm->dev_private;
	struct drm_plane *primary = NULL;
	struct drm_plane *cursor = NULL;
	u32 value;
	int err;

	dc->syncpt = host1x_syncpt_request(dc->dev, flags);
@@ -1755,47 +1799,6 @@ static int tegra_dc_init(struct host1x_client *client)
		goto cleanup;
	}

	/* initialize display controller */
	if (dc->syncpt) {
		u32 syncpt = host1x_syncpt_id(dc->syncpt);

		value = SYNCPT_CNTRL_NO_STALL;
		tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);

		value = SYNCPT_VSYNC_ENABLE | syncpt;
		tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
	}

	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
	tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);

	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
	tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);

	/* initialize timer */
	value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
		WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
	tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);

	value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
		WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
	tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);

	value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
	tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);

	value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
		WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);

	if (dc->soc->supports_border_color)
		tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);

	tegra_dc_stats_reset(&dc->stats);

	return 0;

cleanup:
@@ -1987,33 +1990,15 @@ static int tegra_dc_probe(struct platform_device *pdev)
		return PTR_ERR(dc->rst);
	}

	reset_control_assert(dc->rst);

	if (dc->soc->has_powergate) {
		if (dc->pipe == 0)
			dc->powergate = TEGRA_POWERGATE_DIS;
		else
			dc->powergate = TEGRA_POWERGATE_DISB;

		err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
							dc->rst);
		if (err < 0) {
			dev_err(&pdev->dev, "failed to power partition: %d\n",
				err);
			return err;
		}
	} else {
		err = clk_prepare_enable(dc->clk);
		if (err < 0) {
			dev_err(&pdev->dev, "failed to enable clock: %d\n",
				err);
			return err;
		}

		err = reset_control_deassert(dc->rst);
		if (err < 0) {
			dev_err(&pdev->dev, "failed to deassert reset: %d\n",
				err);
			return err;
		}
		tegra_powergate_power_off(dc->powergate);
	}

	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -2027,16 +2012,19 @@ static int tegra_dc_probe(struct platform_device *pdev)
		return -ENXIO;
	}

	INIT_LIST_HEAD(&dc->client.list);
	dc->client.ops = &dc_client_ops;
	dc->client.dev = &pdev->dev;

	err = tegra_dc_rgb_probe(dc);
	if (err < 0 && err != -ENODEV) {
		dev_err(&pdev->dev, "failed to probe RGB output: %d\n", err);
		return err;
	}

	platform_set_drvdata(pdev, dc);
	pm_runtime_enable(&pdev->dev);

	INIT_LIST_HEAD(&dc->client.list);
	dc->client.ops = &dc_client_ops;
	dc->client.dev = &pdev->dev;

	err = host1x_client_register(&dc->client);
	if (err < 0) {
		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
@@ -2044,8 +2032,6 @@ static int tegra_dc_probe(struct platform_device *pdev)
		return err;
	}

	platform_set_drvdata(pdev, dc);

	return 0;
}

@@ -2067,7 +2053,22 @@ static int tegra_dc_remove(struct platform_device *pdev)
		return err;
	}

	reset_control_assert(dc->rst);
	pm_runtime_disable(&pdev->dev);

	return 0;
}

#ifdef CONFIG_PM
static int tegra_dc_suspend(struct device *dev)
{
	struct tegra_dc *dc = dev_get_drvdata(dev);
	int err;

	err = reset_control_assert(dc->rst);
	if (err < 0) {
		dev_err(dev, "failed to assert reset: %d\n", err);
		return err;
	}

	if (dc->soc->has_powergate)
		tegra_powergate_power_off(dc->powergate);
@@ -2077,10 +2078,45 @@ static int tegra_dc_remove(struct platform_device *pdev)
	return 0;
}

static int tegra_dc_resume(struct device *dev)
{
	struct tegra_dc *dc = dev_get_drvdata(dev);
	int err;

	if (dc->soc->has_powergate) {
		err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
							dc->rst);
		if (err < 0) {
			dev_err(dev, "failed to power partition: %d\n", err);
			return err;
		}
	} else {
		err = clk_prepare_enable(dc->clk);
		if (err < 0) {
			dev_err(dev, "failed to enable clock: %d\n", err);
			return err;
		}

		err = reset_control_deassert(dc->rst);
		if (err < 0) {
			dev_err(dev, "failed to deassert reset: %d\n", err);
			return err;
		}
	}

	return 0;
}
#endif

static const struct dev_pm_ops tegra_dc_pm_ops = {
	SET_RUNTIME_PM_OPS(tegra_dc_suspend, tegra_dc_resume, NULL)
};

struct platform_driver tegra_dc_driver = {
	.driver = {
		.name = "tegra-dc",
		.of_match_table = tegra_dc_of_match,
		.pm = &tegra_dc_pm_ops,
	},
	.probe = tegra_dc_probe,
	.remove = tegra_dc_remove,
+202 −43
Original line number Diff line number Diff line
@@ -12,6 +12,9 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/regulator/consumer.h>
@@ -44,6 +47,11 @@ struct tegra_dpaux {
	struct completion complete;
	struct work_struct work;
	struct list_head list;

#ifdef CONFIG_GENERIC_PINCONF
	struct pinctrl_dev *pinctrl;
	struct pinctrl_desc desc;
#endif
};

static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux)
@@ -267,6 +275,148 @@ static irqreturn_t tegra_dpaux_irq(int irq, void *data)
	return ret;
}

enum tegra_dpaux_functions {
	DPAUX_PADCTL_FUNC_AUX,
	DPAUX_PADCTL_FUNC_I2C,
	DPAUX_PADCTL_FUNC_OFF,
};

static void tegra_dpaux_pad_power_down(struct tegra_dpaux *dpaux)
{
	u32 value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);

	value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;

	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
}

static void tegra_dpaux_pad_power_up(struct tegra_dpaux *dpaux)
{
	u32 value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);

	value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;

	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
}

static int tegra_dpaux_pad_config(struct tegra_dpaux *dpaux, unsigned function)
{
	u32 value;

	switch (function) {
	case DPAUX_PADCTL_FUNC_AUX:
		value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) |
			DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) |
			DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) |
			DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV |
			DPAUX_HYBRID_PADCTL_MODE_AUX;
		break;

	case DPAUX_PADCTL_FUNC_I2C:
		value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV |
			DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV |
			DPAUX_HYBRID_PADCTL_MODE_I2C;
		break;

	case DPAUX_PADCTL_FUNC_OFF:
		tegra_dpaux_pad_power_down(dpaux);
		return 0;

	default:
		return -ENOTSUPP;
	}

	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL);
	tegra_dpaux_pad_power_up(dpaux);

	return 0;
}

#ifdef CONFIG_GENERIC_PINCONF
static const struct pinctrl_pin_desc tegra_dpaux_pins[] = {
	PINCTRL_PIN(0, "DP_AUX_CHx_P"),
	PINCTRL_PIN(1, "DP_AUX_CHx_N"),
};

static const unsigned tegra_dpaux_pin_numbers[] = { 0, 1 };

static const char * const tegra_dpaux_groups[] = {
	"dpaux-io",
};

static const char * const tegra_dpaux_functions[] = {
	"aux",
	"i2c",
	"off",
};

static int tegra_dpaux_get_groups_count(struct pinctrl_dev *pinctrl)
{
	return ARRAY_SIZE(tegra_dpaux_groups);
}

static const char *tegra_dpaux_get_group_name(struct pinctrl_dev *pinctrl,
					      unsigned int group)
{
	return tegra_dpaux_groups[group];
}

static int tegra_dpaux_get_group_pins(struct pinctrl_dev *pinctrl,
				      unsigned group, const unsigned **pins,
				      unsigned *num_pins)
{
	*pins = tegra_dpaux_pin_numbers;
	*num_pins = ARRAY_SIZE(tegra_dpaux_pin_numbers);

	return 0;
}

static const struct pinctrl_ops tegra_dpaux_pinctrl_ops = {
	.get_groups_count = tegra_dpaux_get_groups_count,
	.get_group_name = tegra_dpaux_get_group_name,
	.get_group_pins = tegra_dpaux_get_group_pins,
	.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
	.dt_free_map = pinconf_generic_dt_free_map,
};

static int tegra_dpaux_get_functions_count(struct pinctrl_dev *pinctrl)
{
	return ARRAY_SIZE(tegra_dpaux_functions);
}

static const char *tegra_dpaux_get_function_name(struct pinctrl_dev *pinctrl,
						 unsigned int function)
{
	return tegra_dpaux_functions[function];
}

static int tegra_dpaux_get_function_groups(struct pinctrl_dev *pinctrl,
					   unsigned int function,
					   const char * const **groups,
					   unsigned * const num_groups)
{
	*num_groups = ARRAY_SIZE(tegra_dpaux_groups);
	*groups = tegra_dpaux_groups;

	return 0;
}

static int tegra_dpaux_set_mux(struct pinctrl_dev *pinctrl,
			       unsigned int function, unsigned int group)
{
	struct tegra_dpaux *dpaux = pinctrl_dev_get_drvdata(pinctrl);

	return tegra_dpaux_pad_config(dpaux, function);
}

static const struct pinmux_ops tegra_dpaux_pinmux_ops = {
	.get_functions_count = tegra_dpaux_get_functions_count,
	.get_function_name = tegra_dpaux_get_function_name,
	.get_function_groups = tegra_dpaux_get_function_groups,
	.set_mux = tegra_dpaux_set_mux,
};
#endif

static int tegra_dpaux_probe(struct platform_device *pdev)
{
	struct tegra_dpaux *dpaux;
@@ -294,12 +444,15 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
		return -ENXIO;
	}

	if (!pdev->dev.pm_domain) {
		dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux");
		if (IS_ERR(dpaux->rst)) {
		dev_err(&pdev->dev, "failed to get reset control: %ld\n",
			dev_err(&pdev->dev,
				"failed to get reset control: %ld\n",
				PTR_ERR(dpaux->rst));
			return PTR_ERR(dpaux->rst);
		}
	}

	dpaux->clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(dpaux->clk)) {
@@ -315,34 +468,37 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
		return err;
	}

	if (dpaux->rst)
		reset_control_deassert(dpaux->rst);

	dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent");
	if (IS_ERR(dpaux->clk_parent)) {
		dev_err(&pdev->dev, "failed to get parent clock: %ld\n",
			PTR_ERR(dpaux->clk_parent));
		return PTR_ERR(dpaux->clk_parent);
		err = PTR_ERR(dpaux->clk_parent);
		goto assert_reset;
	}

	err = clk_prepare_enable(dpaux->clk_parent);
	if (err < 0) {
		dev_err(&pdev->dev, "failed to enable parent clock: %d\n",
			err);
		return err;
		goto assert_reset;
	}

	err = clk_set_rate(dpaux->clk_parent, 270000000);
	if (err < 0) {
		dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n",
			err);
		return err;
		goto disable_parent_clk;
	}

	dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd");
	if (IS_ERR(dpaux->vdd)) {
		dev_err(&pdev->dev, "failed to get VDD supply: %ld\n",
			PTR_ERR(dpaux->vdd));
		return PTR_ERR(dpaux->vdd);
		err = PTR_ERR(dpaux->vdd);
		goto disable_parent_clk;
	}

	err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0,
@@ -350,7 +506,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
	if (err < 0) {
		dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n",
			dpaux->irq, err);
		return err;
		goto disable_parent_clk;
	}

	disable_irq(dpaux->irq);
@@ -360,7 +516,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)

	err = drm_dp_aux_register(&dpaux->aux);
	if (err < 0)
		return err;
		goto disable_parent_clk;

	/*
	 * Assume that by default the DPAUX/I2C pads will be used for HDMI,
@@ -370,16 +526,24 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
	 * is no possibility to perform the I2C mode configuration in the
	 * HDMI path.
	 */
	value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
	value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
	err = tegra_dpaux_pad_config(dpaux, DPAUX_HYBRID_PADCTL_MODE_I2C);
	if (err < 0)
		return err;

	value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_PADCTL);
	value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV |
		DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV |
		DPAUX_HYBRID_PADCTL_MODE_I2C;
	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL);
#ifdef CONFIG_GENERIC_PINCONF
	dpaux->desc.name = dev_name(&pdev->dev);
	dpaux->desc.pins = tegra_dpaux_pins;
	dpaux->desc.npins = ARRAY_SIZE(tegra_dpaux_pins);
	dpaux->desc.pctlops = &tegra_dpaux_pinctrl_ops;
	dpaux->desc.pmxops = &tegra_dpaux_pinmux_ops;
	dpaux->desc.owner = THIS_MODULE;

	dpaux->pinctrl = devm_pinctrl_register(&pdev->dev, &dpaux->desc, dpaux);
	if (!dpaux->pinctrl) {
		dev_err(&pdev->dev, "failed to register pincontrol\n");
		return -ENODEV;
	}
#endif
	/* enable and clear all interrupts */
	value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT |
		DPAUX_INTR_UNPLUG_EVENT | DPAUX_INTR_PLUG_EVENT;
@@ -393,17 +557,24 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
	platform_set_drvdata(pdev, dpaux);

	return 0;

disable_parent_clk:
	clk_disable_unprepare(dpaux->clk_parent);
assert_reset:
	if (dpaux->rst)
		reset_control_assert(dpaux->rst);

	clk_disable_unprepare(dpaux->clk);

	return err;
}

static int tegra_dpaux_remove(struct platform_device *pdev)
{
	struct tegra_dpaux *dpaux = platform_get_drvdata(pdev);
	u32 value;

	/* make sure pads are powered down when not in use */
	value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
	value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
	tegra_dpaux_pad_power_down(dpaux);

	drm_dp_aux_unregister(&dpaux->aux);

@@ -414,7 +585,10 @@ static int tegra_dpaux_remove(struct platform_device *pdev)
	cancel_work_sync(&dpaux->work);

	clk_disable_unprepare(dpaux->clk_parent);

	if (dpaux->rst)
		reset_control_assert(dpaux->rst);

	clk_disable_unprepare(dpaux->clk);

	return 0;
@@ -528,30 +702,15 @@ enum drm_connector_status drm_dp_aux_detect(struct drm_dp_aux *aux)
int drm_dp_aux_enable(struct drm_dp_aux *aux)
{
	struct tegra_dpaux *dpaux = to_dpaux(aux);
	u32 value;

	value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) |
		DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) |
		DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) |
		DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV |
		DPAUX_HYBRID_PADCTL_MODE_AUX;
	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL);

	value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
	value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);

	return 0;
	return tegra_dpaux_pad_config(dpaux, DPAUX_PADCTL_FUNC_AUX);
}

int drm_dp_aux_disable(struct drm_dp_aux *aux)
{
	struct tegra_dpaux *dpaux = to_dpaux(aux);
	u32 value;

	value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
	value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN;
	tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE);
	tegra_dpaux_pad_power_down(dpaux);

	return 0;
}
+1 −1
Original line number Diff line number Diff line
@@ -56,8 +56,8 @@ static void tegra_atomic_complete(struct tegra_drm *tegra,
	 */

	drm_atomic_helper_commit_modeset_disables(drm, state);
	drm_atomic_helper_commit_planes(drm, state, false);
	drm_atomic_helper_commit_modeset_enables(drm, state);
	drm_atomic_helper_commit_planes(drm, state, true);

	drm_atomic_helper_wait_for_vblanks(drm, state);

Loading