[PATCH 1/2] drm/msm: Initial add eDP support in msm drm driver (V2)

Thierry Reding thierry.reding at gmail.com
Mon Dec 8 05:28:29 PST 2014


On Fri, Dec 05, 2014 at 04:30:00PM -0500, Hai Li wrote:
[...]
> diff --git a/drivers/gpu/drm/msm/edp/edp.c b/drivers/gpu/drm/msm/edp/edp.c
> new file mode 100644
> index 0000000..32e21e1
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/edp/edp.c
> @@ -0,0 +1,211 @@
> +/*
> + * Copyright (c) 2014, The Linux Foundation. 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 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/of_irq.h>
> +#include "edp.h"
> +
> +static irqreturn_t edp_irq(int irq, void *dev_id)
> +{
> +	struct msm_edp *edp = dev_id;
> +
> +	/* Process eDP irq */
> +	return msm_edp_ctrl_irq(edp->ctrl);
> +}

I find that the architecture of this makes it really difficult to
review. If I want to see what this function does I now have to jump
somewhere else in this patch (over 2000 lines ahead).

> +static void edp_destroy(struct platform_device *pdev)
> +{
> +	struct msm_edp *edp = platform_get_drvdata(pdev);
> +
> +	if (!edp)
> +		return;
> +
> +	if (edp->ctrl) {
> +		msm_edp_ctrl_destroy(edp->ctrl);
> +		edp->ctrl = NULL;
> +	}
> +
> +	platform_set_drvdata(pdev, NULL);
> +
> +	devm_kfree(&pdev->dev, edp);

The whole point of the devm_* functions is that you don't have to clean
them up manually. Why do you need to call this here?

> +}
> +
> +/* construct hdmi at bind/probe time, grab all the resources. */
> +static struct msm_edp *edp_init(struct platform_device *pdev)
> +{
> +	struct msm_edp *edp = NULL;
> +	int ret;
> +
> +	if (!pdev) {
> +		pr_err("no edp device\n");

s/edp/eDP/ here and in a few other places that I haven't pointed out
explicitly.

> +		ret = -ENXIO;
> +		goto fail;
> +	}
> +
> +	edp = devm_kzalloc(&pdev->dev, sizeof(*edp), GFP_KERNEL);
> +	if (!edp) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +	DBG("edp probed=%p", edp);
> +
> +	edp->pdev = pdev;
> +	platform_set_drvdata(pdev, edp);
> +
> +	ret = msm_edp_ctrl_init(edp);
> +	if (ret)
> +		goto fail;
> +
> +	return edp;
> +
> +fail:
> +	if (edp)
> +		edp_destroy(pdev);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +static int edp_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct msm_drm_private *priv = drm->dev_private;
> +	struct msm_edp *edp;
> +
> +	DBG("");
> +	edp = edp_init(to_platform_device(dev));

There's a lot of this casting to platform devices and then using
pdev->dev to get at the struct device. I don't immediately see a use for
the platform device, so why not just stick with struct device *
consistently?

> +	if (IS_ERR(edp))
> +		return PTR_ERR(edp);
> +	priv->edp = edp;
> +
> +	return 0;
> +}
> +
> +static void edp_unbind(struct device *dev, struct device *master,
> +		void *data)

We typically align parameters on subsequent lines with the first
parameter on the first line. But perhaps Rob doesn't care so much.

> +static const struct of_device_id dt_match[] = {
> +	{ .compatible = "qcom,mdss-edp" },
> +	{}
> +};

Don't you want a MODULE_DEVICE_TABLE here?

> +/* Second part of initialization, the drm/kms level modeset_init */
> +int msm_edp_modeset_init(struct msm_edp *edp,
> +		struct drm_device *dev, struct drm_encoder *encoder)
> +{
> +	struct platform_device *pdev = edp->pdev;
> +	struct msm_drm_private *priv = dev->dev_private;
> +	int ret;
> +
> +	edp->encoder = encoder;
> +	edp->dev = dev;
> +
> +	edp->bridge = msm_edp_bridge_init(edp);
> +	if (IS_ERR(edp->bridge)) {
> +		ret = PTR_ERR(edp->bridge);
> +		dev_err(dev->dev, "failed to create eDP bridge: %d\n", ret);
> +		edp->bridge = NULL;
> +		goto fail;
> +	}
> +
> +	edp->connector = msm_edp_connector_init(edp);
> +	if (IS_ERR(edp->connector)) {
> +		ret = PTR_ERR(edp->connector);
> +		dev_err(dev->dev, "failed to create eDP connector: %d\n", ret);
> +		edp->connector = NULL;
> +		goto fail;
> +	}
> +
> +	edp->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);

Why not use the more idiomatic platform_get_irq()?

> +	if (edp->irq < 0) {
> +		ret = edp->irq;
> +		dev_err(dev->dev, "failed to get irq: %d\n", ret);

s/irq/IRQ/

> diff --git a/drivers/gpu/drm/msm/edp/edp.h b/drivers/gpu/drm/msm/edp/edp.h
[...]
> +#ifndef __EDP_CONNECTOR_H__
> +#define __EDP_CONNECTOR_H__
> +
> +#include <linux/clk.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/of_gpio.h>
> +#include <linux/pwm.h>
> +#include <linux/i2c.h>

These should be alphabetically sorted.

> diff --git a/drivers/gpu/drm/msm/edp/edp_aux.c b/drivers/gpu/drm/msm/edp/edp_aux.c
[...]
> +#define to_edp_aux(x) container_of(x, struct edp_aux, drm_aux)

Perhaps this should be a static inline function for better type safety.

> +static int edp_msg_fifo_tx(struct edp_aux *aux, struct drm_dp_aux_msg *msg)
> +{
> +	u32 data[4];
> +	u32 reg, len;
> +	bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
> +	bool read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
> +	u8 *msgdata = msg->buffer;
> +	int i;
> +
> +	if (read)
> +		len = 4;
> +	else
> +		len = msg->size + 4;
> +
> +	/*
> +	 * cmd fifo only has depth of 144 bytes
> +	 */
> +	if (len > AUX_CMD_FIFO_LEN)
> +		return -EINVAL;
> +
> +	/* Pack cmd and write to HW */
> +	data[0] = (msg->address >> 16) & 0xf;	/* addr[19:16] */
> +	if (read)
> +		data[0] |=  BIT(4);		/* R/W */
> +
> +	data[1] = (msg->address >> 8) & 0xff;	/* addr[15:8] */
> +	data[2] = msg->address & 0xff;		/* addr[7:0] */
> +	data[3] = (msg->size - 1) & 0xff;	/* len[7:0] */
> +
> +	for (i = 0; i < len; i++) {
> +		reg = (i < 4) ? data[i] : msgdata[i - 4];
> +		reg = EDP_AUX_DATA_DATA(reg); /* index = 0, write */
> +		if (i == 0)
> +			reg |= EDP_AUX_DATA_INDEX_WRITE;
> +		edp_write(aux->base + REG_EDP_AUX_DATA, reg);
> +
> +		/* Write data 1 by 1 into the FIFO */
> +		wmb();

I don't understand why you think you need these. You already use the
right accessors and they already provide correct barriers. Are you
really sure you need them?

> +	}
> +
> +	reg = 0; /* Transaction number is always 1 */
> +	if (!native) /* i2c */
> +		reg |= EDP_AUX_TRANS_CTRL_I2C;
> +
> +	reg |= EDP_AUX_TRANS_CTRL_GO;
> +	edp_write(aux->base + REG_EDP_AUX_TRANS_CTRL, reg);
> +
> +	/* Make sure transaction is triggered */
> +	wmb();

Same here... and in various other places.

> +/*
> + * This function does the real job to process an aux transaction.

s/aux/AUX/

> + * It will call msm_edp_aux_ctrl() function to reset the aux channel,
> + * if the waiting is timeout.
> + * The caller who triggers the transaction should avoid the
> + * msm_edp_aux_ctrl() running concurrently in other threads, i.e.
> + * start transaction only when aux channel is fully enabled.
> + */
> +ssize_t edp_aux_transfer(struct drm_dp_aux *drm_aux, struct drm_dp_aux_msg *msg)
> +{
> +	struct edp_aux *aux = to_edp_aux(drm_aux);
> +	ssize_t ret;
> +	bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
> +	bool read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);

These checks are confusing. It seems like they might actually work
because of how these symbols are defined, but I'd expect something like:

	native = msg->request & (DP_AUX_NATIVE_WRITE | DP_AUX_NATIVE_READ);

Or perhaps even clearer:

	switch (msg->request) {
	case DP_AUX_NATIVE_WRITE:
	case DP_AUX_NATIVE_READ:
		native = true;
		break;

	...
	}

> +	/* Ignore address only message */
> +	if ((msg->size == 0) || (msg->buffer == NULL)) {
> +		msg->reply = native ?
> +			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
> +		return msg->size;
> +	}

How do you support I2C-over-AUX then? How else will the device know
which I2C slave to address?

> diff --git a/drivers/gpu/drm/msm/edp/edp_bridge.c b/drivers/gpu/drm/msm/edp/edp_bridge.c
[...]
> +static const struct drm_bridge_funcs edp_bridge_funcs = {
> +		.pre_enable = edp_bridge_pre_enable,
> +		.enable = edp_bridge_enable,
> +		.disable = edp_bridge_disable,
> +		.post_disable = edp_bridge_post_disable,
> +		.mode_set = edp_bridge_mode_set,
> +		.destroy = edp_bridge_destroy,
> +		.mode_fixup = edp_bridge_mode_fixup,

These should be indented using a single tab.

> diff --git a/drivers/gpu/drm/msm/edp/edp_connector.c b/drivers/gpu/drm/msm/edp/edp_connector.c
[...]
> +static int edp_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct edp_connector *edp_connector = to_edp_connector(connector);
> +	struct msm_edp *edp = edp_connector->edp;
> +
> +	struct edid *drm_edid = NULL;
> +	int ret = 0;
> +
> +	DBG("");
> +	ret = msm_edp_ctrl_get_edid(edp->ctrl, connector, &drm_edid);
> +	if (ret)
> +		return ret;
> +
> +	if (drm_edid) {
> +		drm_mode_connector_update_edid_property(connector, drm_edid);

I think you want to call this unconditionally to make sure the EDID
property is cleared if you couldn't get a new one. Otherwise you'll end
up with a stale EDID in sysfs.

> +		ret = drm_add_edid_modes(connector, drm_edid);
> +	}
> +
> +	return ret;
> +}
[...]
> +static const struct drm_connector_funcs edp_connector_funcs = {
> +	.dpms = drm_helper_connector_dpms,
> +	.detect = edp_connector_detect,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.destroy = edp_connector_destroy,
> +};
> +
> +static const struct drm_connector_helper_funcs edp_connector_helper_funcs = {
> +	.get_modes = edp_connector_get_modes,
> +	.mode_valid = edp_connector_mode_valid,
> +	.best_encoder = edp_connector_best_encoder,
> +};

This is missing mandatory callbacks for atomic modesetting, isn't this
going to simply crash when applied on top of a recent kernel with atomic
modesetting support?

> +
> +/* initialize connector */
> +struct drm_connector *msm_edp_connector_init(struct msm_edp *edp)
> +{
> +	struct drm_connector *connector = NULL;
> +	struct edp_connector *edp_connector;
> +	int ret;
> +
> +	edp_connector = kzalloc(sizeof(*edp_connector), GFP_KERNEL);
> +	if (!edp_connector) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	edp_connector->edp = edp;
> +
> +	connector = &edp_connector->base;
> +
> +	ret = drm_connector_init(edp->dev, connector, &edp_connector_funcs,
> +			DRM_MODE_CONNECTOR_eDP);
> +	if (ret)
> +		goto fail;
> +
> +	drm_connector_helper_add(connector, &edp_connector_helper_funcs);
> +
> +	/* We don't support HPD, so only poll status until connected. */
> +	connector->polled = DRM_CONNECTOR_POLL_CONNECT;
> +
> +	/* Display driver doesn't support interlace now. */
> +	connector->interlace_allowed = 0;
> +	connector->doublescan_allowed = 0;

These are boolean, so their value should be false rather than 0.

> diff --git a/drivers/gpu/drm/msm/edp/edp_ctrl.c b/drivers/gpu/drm/msm/edp/edp_ctrl.c
[...]
> +#include <linux/kernel.h>
> +#include <linux/gpio.h>
> +#include <linux/leds.h>
> +#include "edp.h"
> +#include "edp.xml.h"
> +#include "drm_dp_helper.h"
> +#include "drm_edid.h"
> +#include "drm_crtc.h"

I think a more natural ordering would be linux/*, drm_*, edp.*, because
that's most generic to most specific.

> +#define RGB_COMPONENTS		3

In my opinion this is overkill. Just use a literal 3 in the two places
where this is actually used. The context is enough to know what this is
for.

> +static int cont_splash;	/* 1 to enable continuous splash screen */
> +EXPORT_SYMBOL(cont_splash);
> +
> +module_param(cont_splash, int, 0);
> +MODULE_PARM_DESC(cont_splash, "Enable continuous splash screen on eDP");

Huh? Is this supposed to allow hand-off from firmware to kernel? If so I
don't think that's going to work without having proper support for it
across the driver. I don't see support for this in the MDP subdriver, so
I doubt that it's going to work at all.

Either way, I don't think using a module parameter for this is the right
solution.

> +struct edp_ctrl {
> +	struct platform_device *pdev;
> +
> +	void __iomem *base;
> +
> +	/* regulators */
> +	struct regulator *vdda_vreg;
> +	struct regulator *lvl_reg;
> +
> +	/* clocks */
> +	struct clk *aux_clk;
> +	struct clk *pixel_clk;
> +	struct clk *ahb_clk;
> +	struct clk *link_clk;
> +	struct clk *mdp_core_clk;
> +
> +	/* gpios */
> +	int gpio_panel_en;
> +	int gpio_panel_hpd;
> +	int gpio_lvl_en;
> +	int gpio_bkl_en;

These should really be using the new gpiod_*() API. Also, at least
panel_en and bkl_en seem wrongly placed. They should be handled in the
panel and backlight drivers, not the eDP driver.

Also it seems like gpio_lvl_en and lvl_reg are two different ways of
representing the same thing. You should use the regulator only and if
it's a simple GPIO use the fixed regulator with a gpio property.

> +
> +	/* backlight */
> +	struct pwm_device *bl_pwm;
> +	u32 pwm_period;
> +	u32 bl_level;
> +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
> +	struct backlight_device *backlight_dev;
> +#endif

This looks very much like a duplicate of pwm-backlight. Any reason why
you can't use it?

> +struct edp_pixel_clk_div {
> +	u32 rate; /* in kHz */
> +	u32 m;
> +	u32 n;
> +};
> +
> +#define EDP_PIXEL_CLK_NUM 8
> +static const struct edp_pixel_clk_div clk_divs[2][EDP_PIXEL_CLK_NUM] = {
> +	{ /* Link clock = 162MHz, source clock = 810MHz */
> +		{119000, 31,  211}, /* WSXGA+ 1680x1050 at 60Hz CVT */
> +		{130250, 32,  199}, /* UXGA 1600x1200 at 60Hz CVT */
> +		{148500, 11,  60},  /* FHD 1920x1080 at 60Hz */
> +		{154000, 50,  263}, /* WUXGA 1920x1200 at 60Hz CVT */
> +		{209250, 31,  120}, /* QXGA 2048x1536 at 60Hz CVT */
> +		{268500, 119, 359}, /* WQXGA 2560x1600 at 60Hz CVT */
> +		{138530, 33,  193}, /* AUO B116HAN03.0 Panel */
> +		{141400, 48,  275}, /* AUO B133HTN01.2 Panel */
> +	},
> +	{ /* Link clock = 270MHz, source clock = 675MHz */
> +		{119000, 52,  295}, /* WSXGA+ 1680x1050 at 60Hz CVT */
> +		{130250, 11,  57},  /* UXGA 1600x1200 at 60Hz CVT */
> +		{148500, 11,  50},  /* FHD 1920x1080 at 60Hz */
> +		{154000, 47,  206}, /* WUXGA 1920x1200 at 60Hz CVT */
> +		{209250, 31,  100}, /* QXGA 2048x1536 at 60Hz CVT */
> +		{268500, 107, 269}, /* WQXGA 2560x1600 at 60Hz CVT */
> +		{138530, 63,  307}, /* AUO B116HAN03.0 Panel */
> +		{141400, 53,  253}, /* AUO B133HTN01.2 Panel */
> +	},
> +};

Can't you compute these programmatically? If you rely on this table
you'll need to extend it everytime you want to support a new panel or
resolution.

> +static void edp_clk_deinit(struct edp_ctrl *ctrl)
> +{
> +	struct device *dev = &ctrl->pdev->dev;
> +
> +	if (ctrl->aux_clk)
> +		devm_clk_put(dev, ctrl->aux_clk);
> +	if (ctrl->pixel_clk)
> +		devm_clk_put(dev, ctrl->pixel_clk);
> +	if (ctrl->ahb_clk)
> +		devm_clk_put(dev, ctrl->ahb_clk);
> +	if (ctrl->link_clk)
> +		devm_clk_put(dev, ctrl->link_clk);
> +	if (ctrl->mdp_core_clk)
> +		devm_clk_put(dev, ctrl->mdp_core_clk);
> +}

What's the point of using devm_* if you do manual cleanup anyway?

> +	ctrl->mdp_core_clk = devm_clk_get(dev, "mdp_core_clk");
> +	if (IS_ERR(ctrl->mdp_core_clk)) {
> +		pr_err("%s: Can't find mdp_core_clk", __func__);
> +		ctrl->mdp_core_clk = NULL;
> +		goto edp_clk_err;
> +	}
> +
> +	return 0;
> +
> +edp_clk_err:
> +	edp_clk_deinit(ctrl);
> +	return -EPERM;

You should really propagate a proper error code here.

> +static int edp_regulator_init(struct edp_ctrl *ctrl)
> +{
> +	struct device *dev = &ctrl->pdev->dev;
> +	int ret;
> +
> +	DBG("");
> +	ctrl->vdda_vreg = devm_regulator_get(dev, "vdda");
> +	if (IS_ERR(ctrl->vdda_vreg)) {
> +		pr_err("%s: Could not get vdda reg, ret = %ld\n", __func__,
> +				PTR_ERR(ctrl->vdda_vreg));
> +		ctrl->vdda_vreg = NULL;
> +		ret = -ENODEV;
> +		goto f0;
> +	}
> +
> +	ret = regulator_set_voltage(ctrl->vdda_vreg, VDDA_MIN_UV, VDDA_MAX_UV);
> +	if (ret) {
> +		pr_err("%s:vdda_vreg set_voltage failed, %d\n", __func__, ret);
> +		goto f1;
> +	}
> +
> +	ret = regulator_set_optimum_mode(ctrl->vdda_vreg, VDDA_UA_ON_LOAD);
> +	if (ret < 0) {
> +		pr_err("%s: vdda_vreg set regulator mode failed.\n", __func__);
> +		goto f1;
> +	}
> +
> +	ret = regulator_enable(ctrl->vdda_vreg);
> +	if (ret) {
> +		pr_err("%s: Failed to enable vdda_vreg regulator.\n", __func__);
> +		goto f2;
> +	}
> +
> +	DBG("exit");
> +	return 0;
> +
> +f2:
> +	regulator_set_optimum_mode(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD);
> +f1:
> +	devm_regulator_put(ctrl->vdda_vreg);
> +	ctrl->vdda_vreg = NULL;
> +f0:
> +	return ret;

The label names could be improved here.

> +/* The power source of the level translation chip is different on different
> + * target boards, i.e. a gpio or a regulator.
> + */

Like I said above you should simply always make this a regulator and use
a fixed GPIO regulator if it's controlled by a GPIO.

> +static int edp_sink_power_state(struct edp_ctrl *ctrl, u8 state)
> +{
> +	u8 s = state;
> +
> +	DBG("%d", s);
> +
> +	if (ctrl->dp_link.revision < 0x11)
> +		return 0;
> +
> +	if (drm_dp_dpcd_write(ctrl->drm_aux, DP_SET_POWER, &s, 1) < 1) {
> +		pr_err("%s: Set power state to panel failed\n", __func__);
> +		return -ENOLINK;
> +	}
> +
> +	return 0;
> +}

This is essentially drm_dp_link_power_up()/drm_dp_link_power_down().
Please use common code where available. And if it's not available yet
the code is completely generic, please add a core function so that
other drivers can reuse it.

> +static int edp_train_pattern_set_write(struct edp_ctrl *ctrl, u8 pattern)
> +{
> +	u8 p = pattern;
> +
> +	DBG("pattern=%x", p);
> +	if (drm_dp_dpcd_write(ctrl->drm_aux, 0x102, &p, 1) < 1) {

0x102 is DP_TRAINING_PATTERN_SET.

> +static void edp_host_train_set(struct edp_ctrl *ctrl, u32 train)
> +{
> +	int cnt;
> +	u32 data;
> +	u32 bit;
> +
> +	bit = 1;
> +	bit <<= (train - 1);
> +	DBG("%s: bit=%d train=%d", __func__, bit, train);
> +
> +	edp_state_ctrl(ctrl, bit);
> +	bit = 8;
> +	bit <<= (train - 1);
> +	cnt = 10;

Maybe do that as part of the declaration?

> +	while (cnt--) {
> +		data = edp_read(ctrl->base + REG_EDP_MAINLINK_READY);
> +		if (data & bit)
> +			break;
> +	}
> +
> +	if (cnt == 0)
> +		pr_err("%s: set link_train=%d failed\n", __func__, train);

I don't think this works as was intended. while (cnt--) will still
execute the loop once because the post-fix operator is applied after the
variable is evaluated. That is, after the loop terminates, cnt will
really be -1, so the error won't be printed. It will only be printed if
the loop happens to terminate on the penultimate iteration.

> +	int tries;
> +	int ret = 0;
> +	int rlen;
> +
> +	DBG("");
> +
> +	edp_host_train_set(ctrl, 0x02); /* train_2 */

Perhaps use the DP_TRAINING_PATTERN_* symbolic names and avoid the
comment.

> +static int edp_ctrl_training(struct edp_ctrl *ctrl)
> +{
> +	int ret;
> +
> +	/* Do link training only when power is on */
> +	if (ctrl->cont_splash || (!ctrl->power_on))

No need for the parentheses around !ctrl->power_on.

> +static void edp_ctrl_on_worker(struct work_struct *work)
> +{
[...]
> +}
> +
> +static void edp_ctrl_off_worker(struct work_struct *work)
> +{
> +	struct edp_ctrl *ctrl = container_of(
> +				work, struct edp_ctrl, off_work);
> +	int ret = 0;

No need to initialize this.

[...]
> +}

Why are these two functions workers?

> +
> +irqreturn_t msm_edp_ctrl_irq(struct edp_ctrl *ctrl)
> +{
[...]
> +	if (isr1 & EDP_INTERRUPT_REG_1_HPD)
> +		DBG("edp_hpd");

Don't you want to handle this?

> +
> +	if (isr2 & EDP_INTERRUPT_REG_2_READY_FOR_VIDEO)
> +		DBG("edp_video_ready");
> +
> +	if (isr2 & EDP_INTERRUPT_REG_2_IDLE_PATTERNs_SENT) {

s/PATTERNs/PATTERNS/? I was going to make that comment to the definition
of this define, but I can't seem to find it. I suspect that it comes
from one of the generated headers, but I can't seem to find either the
generated header nor the XML.

> +		DBG("idle_patterns_sent");
> +		complete(&ctrl->idle_comp);
> +	}
> +
> +	msm_edp_aux_irq(ctrl->aux, isr1);
> +
> +	return IRQ_HANDLED;
> +}
[...]
> +bool msm_edp_ctrl_panel_connected(struct edp_ctrl *ctrl)
> +{
> +	bool ret;

This is unnecessary, the only place where this is used is to return the
value of ctrl->edp_connected. You can use that directly instead.

[...]
> +	ret = ctrl->edp_connected;
> +	mutex_unlock(&ctrl->dev_mutex);
> +
> +	return ret;
> +}
> +
> +int msm_edp_ctrl_get_edid(struct edp_ctrl *ctrl,
> +		struct drm_connector *connector, struct edid **edid)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&ctrl->dev_mutex);
> +
> +	if (ctrl->edid) {
> +		if (edid) {
> +			DBG("Just return edid buffer");
> +			*edid = ctrl->edid;
> +		}
> +		goto unlock_ret;
> +	}

Is there a way to invalidate an existing EDID?

> +
> +	if (!ctrl->power_on) {
> +		if (!ctrl->cont_splash)
> +			edp_ctrl_phy_aux_enable(ctrl, 1);
> +		edp_ctrl_irq_enable(ctrl, 1);
> +	}
> +
> +	ret = drm_dp_link_probe(ctrl->drm_aux, &ctrl->dp_link);
> +	if (ret) {
> +		pr_err("%s: read dpcd cap failed, %d\n", __func__, ret);
> +		goto disable_ret;
> +	}
> +
> +	/* Initialize link rate as panel max link rate */
> +	ctrl->link_rate = drm_dp_link_rate_to_bw_code(ctrl->dp_link.rate);

There's a lot of code here that should probably be a separate function
rather than be called as part of retrieving the EDID.

> +int msm_edp_ctrl_timing_cfg(struct edp_ctrl *ctrl,
> +	struct drm_display_mode *mode, struct drm_display_info *info)

Can mode and info be const?

> +{
> +	u32 hstart_from_sync, vstart_from_sync;
> +	u32 data;
> +	int ret = 0;
> +
[...]
> +
> +	vstart_from_sync = mode->vtotal - mode->vsync_start;
> +	hstart_from_sync = (mode->htotal - mode->hsync_start);

No need for the parentheses.

> diff --git a/drivers/gpu/drm/msm/edp/edp_phy.c b/drivers/gpu/drm/msm/edp/edp_phy.c
[...]
> +bool msm_edp_phy_ready(struct edp_phy *phy)
> +{
> +	u32 status;
> +	int cnt;
> +
> +	cnt = 100;
> +	while (--cnt) {
> +		status = edp_read(phy->base +
> +				REG_EDP_PHY_GLB_PHY_STATUS);
> +		if (status & 0x01)

Can you add a define for 0x01?

> +			break;
> +		usleep_range(500, 1000);
> +	}
> +
> +	if (cnt <= 0) {

This is a better version than above, except that cnt can never be
negative. It will be zero upon timeout.

> +		pr_err("%s: PHY NOT ready\n", __func__);
> +		return false;
> +	} else {
> +		return true;
> +	}
> +}
> +
> +void msm_edp_phy_ctrl(struct edp_phy *phy, int enable)
> +{
> +	DBG("enable=%d", enable);
> +	if (enable) {
> +		/* Reset */
> +		edp_write(phy->base + REG_EDP_PHY_CTRL, 0x005); /* bit 0, 2 */
> +		wmb();
> +		usleep_range(500, 1000);
> +		edp_write(phy->base + REG_EDP_PHY_CTRL, 0x000);
> +		edp_write(phy->base + REG_EDP_PHY_GLB_PD_CTL, 0x3f);
> +		edp_write(phy->base + REG_EDP_PHY_GLB_CFG, 0x1);
> +	} else {
> +		edp_write(phy->base + REG_EDP_PHY_GLB_PD_CTL, 0xc0);
> +	}

Please, also add defines for the values here. It's impossible to tell
from the code what this does or what might need fixing if there was a
bug.

> +void msm_edp_phy_lane_power_ctrl(struct edp_phy *phy, int up, int max_lane)

bool for up? And unsigned int for max_lane?

> +{
> +	int i;

This could also be unsigned int.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/dri-devel/attachments/20141208/8f94faa8/attachment-0001.sig>


More information about the dri-devel mailing list