Commit 3e6f73b8 authored by Dave Airlie's avatar Dave Airlie
Browse files

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



drm/tegra: Changes for v5.10-rc1

This is a handful of patches that add bridge support for Tegra devices
and fix a couple of minor issues.

Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Thierry Reding <thierry.reding@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200921121245.3953659-1-thierry.reding@gmail.com
parents c03156d7 d9f980eb
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include <linux/gpio/consumer.h>

#include <drm/drm_atomic.h>
#include <drm/drm_bridge.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
#include <drm/drm_fb_helper.h>
@@ -116,6 +117,7 @@ struct tegra_output {
	struct device_node *of_node;
	struct device *dev;

	struct drm_bridge *bridge;
	struct drm_panel *panel;
	struct i2c_adapter *ddc;
	const struct edid *edid;
+18 −6
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
 */

#include <drm/drm_atomic_helper.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_simple_kms_helper.h>

@@ -99,27 +100,38 @@ int tegra_output_probe(struct tegra_output *output)
	if (!output->of_node)
		output->of_node = output->dev->of_node;

	err = drm_of_find_panel_or_bridge(output->of_node, -1, -1,
					  &output->panel, &output->bridge);
	if (err && err != -ENODEV)
		return err;

	panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
	if (panel) {
		/*
		 * Don't mix nvidia,panel phandle with the graph in a
		 * device-tree.
		 */
		WARN_ON(output->panel || output->bridge);

		output->panel = of_drm_find_panel(panel);
		of_node_put(panel);

		if (IS_ERR(output->panel))
			return PTR_ERR(output->panel);

		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);
		output->ddc = of_get_i2c_adapter_by_node(ddc);
		of_node_put(ddc);

		if (!output->ddc) {
			err = -EPROBE_DEFER;
			of_node_put(ddc);
			return err;
		}

		of_node_put(ddc);
	}

	output->hpd_gpio = devm_gpiod_get_from_of_node(output->dev,
@@ -173,7 +185,7 @@ void tegra_output_remove(struct tegra_output *output)
		free_irq(output->hpd_irq, output);

	if (output->ddc)
		put_device(&output->ddc->dev);
		i2c_put_adapter(output->ddc);
}

int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
+54 −48
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@
#include <linux/clk.h>

#include <drm/drm_atomic_helper.h>
#include <drm/drm_panel.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_simple_kms_helper.h>

#include "drm.h"
@@ -85,45 +85,13 @@ static void tegra_dc_write_regs(struct tegra_dc *dc,
		tegra_dc_writel(dc, table[i].value, table[i].offset);
}

static const struct drm_connector_funcs tegra_rgb_connector_funcs = {
	.reset = drm_atomic_helper_connector_reset,
	.detect = tegra_output_connector_detect,
	.fill_modes = drm_helper_probe_single_connector_modes,
	.destroy = tegra_output_connector_destroy,
	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};

static enum drm_mode_status
tegra_rgb_connector_mode_valid(struct drm_connector *connector,
			       struct drm_display_mode *mode)
{
	/*
	 * FIXME: For now, always assume that the mode is okay. There are
	 * unresolved issues with clk_round_rate(), which doesn't always
	 * reliably report whether a frequency can be set or not.
	 */
	return MODE_OK;
}

static const struct drm_connector_helper_funcs tegra_rgb_connector_helper_funcs = {
	.get_modes = tegra_output_connector_get_modes,
	.mode_valid = tegra_rgb_connector_mode_valid,
};

static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)
{
	struct tegra_output *output = encoder_to_output(encoder);
	struct tegra_rgb *rgb = to_rgb(output);

	if (output->panel)
		drm_panel_disable(output->panel);

	tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
	tegra_dc_commit(rgb->dc);

	if (output->panel)
		drm_panel_unprepare(output->panel);
}

static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
@@ -132,9 +100,6 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
	struct tegra_rgb *rgb = to_rgb(output);
	u32 value;

	if (output->panel)
		drm_panel_prepare(output->panel);

	tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable));

	value = DE_SELECT_ACTIVE | DE_CONTROL_NORMAL;
@@ -156,9 +121,6 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
	tegra_dc_writel(rgb->dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);

	tegra_dc_commit(rgb->dc);

	if (output->panel)
		drm_panel_enable(output->panel);
}

static int
@@ -267,24 +229,68 @@ int tegra_dc_rgb_remove(struct tegra_dc *dc)
int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
{
	struct tegra_output *output = dc->rgb;
	struct drm_connector *connector;
	int err;

	if (!dc->rgb)
		return -ENODEV;

	drm_connector_init(drm, &output->connector, &tegra_rgb_connector_funcs,
			   DRM_MODE_CONNECTOR_LVDS);
	drm_connector_helper_add(&output->connector,
				 &tegra_rgb_connector_helper_funcs);
	output->connector.dpms = DRM_MODE_DPMS_OFF;

	drm_simple_encoder_init(drm, &output->encoder, DRM_MODE_ENCODER_LVDS);
	drm_encoder_helper_add(&output->encoder,
			       &tegra_rgb_encoder_helper_funcs);

	drm_connector_attach_encoder(&output->connector,
					  &output->encoder);
	drm_connector_register(&output->connector);
	/*
	 * Wrap directly-connected panel into DRM bridge in order to let
	 * DRM core to handle panel for us.
	 */
	if (output->panel) {
		output->bridge = devm_drm_panel_bridge_add(output->dev,
							   output->panel);
		if (IS_ERR(output->bridge)) {
			dev_err(output->dev,
				"failed to wrap panel into bridge: %pe\n",
				output->bridge);
			return PTR_ERR(output->bridge);
		}

		output->panel = NULL;
	}

	/*
	 * Tegra devices that have LVDS panel utilize LVDS encoder bridge
	 * for converting up to 28 LCD LVTTL lanes into 5/4 LVDS lanes that
	 * go to display panel's receiver.
	 *
	 * Encoder usually have a power-down control which needs to be enabled
	 * in order to transmit data to the panel.  Historically devices that
	 * use an older device-tree version didn't model the bridge, assuming
	 * that encoder is turned ON by default, while today's DRM allows us
	 * to model LVDS encoder properly.
	 *
	 * Newer device-trees utilize LVDS encoder bridge, which provides
	 * us with a connector and handles the display panel.
	 *
	 * For older device-trees we wrapped panel into the panel-bridge.
	 */
	if (output->bridge) {
		err = drm_bridge_attach(&output->encoder, output->bridge,
					NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
		if (err) {
			dev_err(output->dev, "failed to attach bridge: %d\n",
				err);
			return err;
		}

		connector = drm_bridge_connector_init(drm, &output->encoder);
		if (IS_ERR(connector)) {
			dev_err(output->dev,
				"failed to initialize bridge connector: %pe\n",
				connector);
			return PTR_ERR(connector);
		}

		drm_connector_attach_encoder(connector, &output->encoder);
	}

	err = tegra_output_init(drm, output);
	if (err < 0) {
+6 −1
Original line number Diff line number Diff line
@@ -3728,7 +3728,12 @@ static int tegra_sor_probe(struct platform_device *pdev)
		if (!sor->aux)
			return -EPROBE_DEFER;

		if (get_device(&sor->aux->ddc.dev)) {
			if (try_module_get(sor->aux->ddc.owner))
				sor->output.ddc = &sor->aux->ddc;
			else
				put_device(&sor->aux->ddc.dev);
		}
	}

	if (!sor->aux) {