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

Commit a76edb8c authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'topic/drm-misc-2015-10-22' of git://anongit.freedesktop.org/drm-intel into drm-next

Few more drm-misc stragglers for 4.4. Big thing is the generic probe for
imx/rockchip/armada (but the variant for msm/rpi/exynos is still missing).

Also the hdmi clocking fixes from Ville which was a lot of confusion about
which tree it should be applied to ;-)

* tag 'topic/drm-misc-2015-10-22' of git://anongit.freedesktop.org/drm-intel:
  drm: correctly check failed allocation
  vga_switcheroo: Constify vga_switcheroo_handler
  drm/armada: Convert the probe function to the generic drm_of_component_probe()
  drm/rockchip: Convert the probe function to the generic drm_of_component_probe()
  drm/imx: Convert the probe function to the generic drm_of_component_probe()
  drm: Introduce generic probe function for component based masters.
  drm/edid: Round to closest when computing the CEA/HDMI alternate clock
  drm/edid: Fix up clock for CEA/HDMI modes specified via detailed timings
parents f1a04d82 48aa1e74
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -501,7 +501,7 @@ static int amdgpu_atpx_get_client_id(struct pci_dev *pdev)
		return VGA_SWITCHEROO_DIS;
}

static struct vga_switcheroo_handler amdgpu_atpx_handler = {
static const struct vga_switcheroo_handler amdgpu_atpx_handler = {
	.switchto = amdgpu_atpx_switchto,
	.power_state = amdgpu_atpx_power_state,
	.init = amdgpu_atpx_init,
+19 −49
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/of_graph.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_of.h>
#include "armada_crtc.h"
#include "armada_drm.h"
#include "armada_gem.h"
@@ -262,43 +263,29 @@ static void armada_add_endpoints(struct device *dev,
	}
}

static int armada_drm_find_components(struct device *dev,
	struct component_match **match)
{
	struct device_node *port;
	int i;

	if (dev->of_node) {
		struct device_node *np = dev->of_node;

		for (i = 0; ; i++) {
			port = of_parse_phandle(np, "ports", i);
			if (!port)
				break;

			component_match_add(dev, match, compare_of, port);
			of_node_put(port);
		}
static const struct component_master_ops armada_master_ops = {
	.bind = armada_drm_bind,
	.unbind = armada_drm_unbind,
};

		if (i == 0) {
			dev_err(dev, "missing 'ports' property\n");
			return -ENODEV;
		}
static int armada_drm_probe(struct platform_device *pdev)
{
	struct component_match *match = NULL;
	struct device *dev = &pdev->dev;
	int ret;

		for (i = 0; ; i++) {
			port = of_parse_phandle(np, "ports", i);
			if (!port)
				break;
	ret = drm_of_component_probe(dev, compare_dev_name, &armada_master_ops);
	if (ret != -EINVAL)
		return ret;

			armada_add_endpoints(dev, match, port);
			of_node_put(port);
		}
	} else if (dev->platform_data) {
	if (dev->platform_data) {
		char **devices = dev->platform_data;
		struct device_node *port;
		struct device *d;
		int i;

		for (i = 0; devices[i]; i++)
			component_match_add(dev, match, compare_dev_name,
			component_match_add(dev, &match, compare_dev_name,
					    devices[i]);

		if (i == 0) {
@@ -311,29 +298,12 @@ static int armada_drm_find_components(struct device *dev,
						    devices[i]);
			if (d && d->of_node) {
				for_each_child_of_node(d->of_node, port)
					armada_add_endpoints(dev, match, port);
					armada_add_endpoints(dev, &match, port);
			}
			put_device(d);
		}
	}

	return 0;
}

static const struct component_master_ops armada_master_ops = {
	.bind = armada_drm_bind,
	.unbind = armada_drm_unbind,
};

static int armada_drm_probe(struct platform_device *pdev)
{
	struct component_match *match = NULL;
	int ret;

	ret = armada_drm_find_components(&pdev->dev, &match);
	if (ret < 0)
		return ret;

	return component_master_add_with_match(&pdev->dev, &armada_master_ops,
					       match);
}
+30 −0
Original line number Diff line number Diff line
@@ -1533,6 +1533,9 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
					  "select subconnector",
					  drm_tv_select_enum_list,
					  ARRAY_SIZE(drm_tv_select_enum_list));
	if (!tv_selector)
		goto nomem;

	dev->mode_config.tv_select_subconnector_property = tv_selector;

	tv_subconnector =
@@ -1540,6 +1543,8 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
				    "subconnector",
				    drm_tv_subconnector_enum_list,
				    ARRAY_SIZE(drm_tv_subconnector_enum_list));
	if (!tv_subconnector)
		goto nomem;
	dev->mode_config.tv_subconnector_property = tv_subconnector;

	/*
@@ -1547,42 +1552,67 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
	 */
	dev->mode_config.tv_left_margin_property =
		drm_property_create_range(dev, 0, "left margin", 0, 100);
	if (!dev->mode_config.tv_left_margin_property)
		goto nomem;

	dev->mode_config.tv_right_margin_property =
		drm_property_create_range(dev, 0, "right margin", 0, 100);
	if (!dev->mode_config.tv_right_margin_property)
		goto nomem;

	dev->mode_config.tv_top_margin_property =
		drm_property_create_range(dev, 0, "top margin", 0, 100);
	if (!dev->mode_config.tv_top_margin_property)
		goto nomem;

	dev->mode_config.tv_bottom_margin_property =
		drm_property_create_range(dev, 0, "bottom margin", 0, 100);
	if (!dev->mode_config.tv_bottom_margin_property)
		goto nomem;

	dev->mode_config.tv_mode_property =
		drm_property_create(dev, DRM_MODE_PROP_ENUM,
				    "mode", num_modes);
	if (!dev->mode_config.tv_mode_property)
		goto nomem;

	for (i = 0; i < num_modes; i++)
		drm_property_add_enum(dev->mode_config.tv_mode_property, i,
				      i, modes[i]);

	dev->mode_config.tv_brightness_property =
		drm_property_create_range(dev, 0, "brightness", 0, 100);
	if (!dev->mode_config.tv_brightness_property)
		goto nomem;

	dev->mode_config.tv_contrast_property =
		drm_property_create_range(dev, 0, "contrast", 0, 100);
	if (!dev->mode_config.tv_contrast_property)
		goto nomem;

	dev->mode_config.tv_flicker_reduction_property =
		drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
	if (!dev->mode_config.tv_flicker_reduction_property)
		goto nomem;

	dev->mode_config.tv_overscan_property =
		drm_property_create_range(dev, 0, "overscan", 0, 100);
	if (!dev->mode_config.tv_overscan_property)
		goto nomem;

	dev->mode_config.tv_saturation_property =
		drm_property_create_range(dev, 0, "saturation", 0, 100);
	if (!dev->mode_config.tv_saturation_property)
		goto nomem;

	dev->mode_config.tv_hue_property =
		drm_property_create_range(dev, 0, "hue", 0, 100);
	if (!dev->mode_config.tv_hue_property)
		goto nomem;

	return 0;
nomem:
	return -ENOMEM;
}
EXPORT_SYMBOL(drm_mode_create_tv_properties);

+50 −2
Original line number Diff line number Diff line
@@ -2418,6 +2418,8 @@ add_cvt_modes(struct drm_connector *connector, struct edid *edid)
	return closure.modes;
}

static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode);

static void
do_detailed_mode(struct detailed_timing *timing, void *c)
{
@@ -2434,6 +2436,13 @@ do_detailed_mode(struct detailed_timing *timing, void *c)
		if (closure->preferred)
			newmode->type |= DRM_MODE_TYPE_PREFERRED;

		/*
		 * Detailed modes are limited to 10kHz pixel clock resolution,
		 * so fix up anything that looks like CEA/HDMI mode, but the clock
		 * is just slightly off.
		 */
		fixup_detailed_cea_mode_clock(newmode);

		drm_mode_probed_add(closure->connector, newmode);
		closure->modes++;
		closure->preferred = 0;
@@ -2529,9 +2538,9 @@ cea_mode_alternate_clock(const struct drm_display_mode *cea_mode)
	 * and the 60Hz variant otherwise.
	 */
	if (cea_mode->vdisplay == 240 || cea_mode->vdisplay == 480)
		clock = clock * 1001 / 1000;
		clock = DIV_ROUND_CLOSEST(clock * 1001, 1000);
	else
		clock = DIV_ROUND_UP(clock * 1000, 1001);
		clock = DIV_ROUND_CLOSEST(clock * 1000, 1001);

	return clock;
}
@@ -3103,6 +3112,45 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)
	return modes;
}

static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode)
{
	const struct drm_display_mode *cea_mode;
	int clock1, clock2, clock;
	u8 mode_idx;
	const char *type;

	mode_idx = drm_match_cea_mode(mode) - 1;
	if (mode_idx < ARRAY_SIZE(edid_cea_modes)) {
		type = "CEA";
		cea_mode = &edid_cea_modes[mode_idx];
		clock1 = cea_mode->clock;
		clock2 = cea_mode_alternate_clock(cea_mode);
	} else {
		mode_idx = drm_match_hdmi_mode(mode) - 1;
		if (mode_idx < ARRAY_SIZE(edid_4k_modes)) {
			type = "HDMI";
			cea_mode = &edid_4k_modes[mode_idx];
			clock1 = cea_mode->clock;
			clock2 = hdmi_mode_alternate_clock(cea_mode);
		} else {
			return;
		}
	}

	/* pick whichever is closest */
	if (abs(mode->clock - clock1) < abs(mode->clock - clock2))
		clock = clock1;
	else
		clock = clock2;

	if (mode->clock == clock)
		return;

	DRM_DEBUG("detailed mode matches %s VIC %d, adjusting clock %d -> %d\n",
		  type, mode_idx + 1, mode->clock, clock);
	mode->clock = clock;
}

static void
parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db)
{
+88 −0
Original line number Diff line number Diff line
#include <linux/component.h>
#include <linux/export.h>
#include <linux/list.h>
#include <linux/of_graph.h>
@@ -61,3 +62,90 @@ uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
	return possible_crtcs;
}
EXPORT_SYMBOL(drm_of_find_possible_crtcs);

/**
 * drm_of_component_probe - Generic probe function for a component based master
 * @dev: master device containing the OF node
 * @compare_of: compare function used for matching components
 * @master_ops: component master ops to be used
 *
 * Parse the platform device OF node and bind all the components associated
 * with the master. Interface ports are added before the encoders in order to
 * satisfy their .bind requirements
 * See Documentation/devicetree/bindings/graph.txt for the bindings.
 *
 * Returns zero if successful, or one of the standard error codes if it fails.
 */
int drm_of_component_probe(struct device *dev,
			   int (*compare_of)(struct device *, void *),
			   const struct component_master_ops *m_ops)
{
	struct device_node *ep, *port, *remote;
	struct component_match *match = NULL;
	int i;

	if (!dev->of_node)
		return -EINVAL;

	/*
	 * Bind the crtc's ports first, so that drm_of_find_possible_crtcs()
	 * called from encoder's .bind callbacks works as expected
	 */
	for (i = 0; ; i++) {
		port = of_parse_phandle(dev->of_node, "ports", i);
		if (!port)
			break;

		if (!of_device_is_available(port->parent)) {
			of_node_put(port);
			continue;
		}

		component_match_add(dev, &match, compare_of, port);
		of_node_put(port);
	}

	if (i == 0) {
		dev_err(dev, "missing 'ports' property\n");
		return -ENODEV;
	}

	if (!match) {
		dev_err(dev, "no available port\n");
		return -ENODEV;
	}

	/*
	 * For bound crtcs, bind the encoders attached to their remote endpoint
	 */
	for (i = 0; ; i++) {
		port = of_parse_phandle(dev->of_node, "ports", i);
		if (!port)
			break;

		if (!of_device_is_available(port->parent)) {
			of_node_put(port);
			continue;
		}

		for_each_child_of_node(port, ep) {
			remote = of_graph_get_remote_port_parent(ep);
			if (!remote || !of_device_is_available(remote)) {
				of_node_put(remote);
				continue;
			} else if (!of_device_is_available(remote->parent)) {
				dev_warn(dev, "parent device of %s is not available\n",
					 remote->full_name);
				of_node_put(remote);
				continue;
			}

			component_match_add(dev, &match, compare_of, remote);
			of_node_put(remote);
		}
		of_node_put(port);
	}

	return component_master_add_with_match(dev, m_ops, match);
}
EXPORT_SYMBOL(drm_of_component_probe);
Loading