From 5b06ba2378e177fdb8f100adda6e55b205308202 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Wed, 11 Jan 2017 12:22:27 +0530 Subject: drm/bridge: adv7511: Initialize regulators Maintain a table of regulator names expected by ADV7511 and ADV7533. Use regulator_bulk_* api to configure these. Initialize and enable the regulators during probe itself. Controlling these dynamically is left for later. Reviewed-by: Laurent Pinchart Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1484117547-26417-3-git-send-email-architt@codeaurora.org --- drivers/gpu/drm/bridge/adv7511/adv7511.h | 4 ++ drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 86 +++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 992d76ce02bb..039147fde7b0 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -329,6 +330,9 @@ struct adv7511 { struct gpio_desc *gpio_pd; + struct regulator_bulk_data *supplies; + unsigned int num_supplies; + /* ADV7533 DSI RX related params */ struct device_node *host_node; struct mipi_dsi_device *dsi; diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 8dba729f6ef9..70b426925604 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -839,6 +839,58 @@ static struct drm_bridge_funcs adv7511_bridge_funcs = { * Probe & remove */ +static const char * const adv7511_supply_names[] = { + "avdd", + "dvdd", + "pvdd", + "bgvdd", + "dvdd-3v", +}; + +static const char * const adv7533_supply_names[] = { + "avdd", + "dvdd", + "pvdd", + "a2vdd", + "v3p3", + "v1p2", +}; + +static int adv7511_init_regulators(struct adv7511 *adv) +{ + struct device *dev = &adv->i2c_main->dev; + const char * const *supply_names; + unsigned int i; + int ret; + + if (adv->type == ADV7511) { + supply_names = adv7511_supply_names; + adv->num_supplies = ARRAY_SIZE(adv7511_supply_names); + } else { + supply_names = adv7533_supply_names; + adv->num_supplies = ARRAY_SIZE(adv7533_supply_names); + } + + adv->supplies = devm_kcalloc(dev, adv->num_supplies, + sizeof(*adv->supplies), GFP_KERNEL); + if (!adv->supplies) + return -ENOMEM; + + for (i = 0; i < adv->num_supplies; i++) + adv->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, adv->num_supplies, adv->supplies); + if (ret) + return ret; + + return regulator_bulk_enable(adv->num_supplies, adv->supplies); +} + +static void adv7511_uninit_regulators(struct adv7511 *adv) +{ + regulator_bulk_disable(adv->num_supplies, adv->supplies); +} + static int adv7511_parse_dt(struct device_node *np, struct adv7511_link_config *config) { @@ -939,6 +991,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (!adv7511) return -ENOMEM; + adv7511->i2c_main = i2c; adv7511->powered = false; adv7511->status = connector_status_disconnected; @@ -956,13 +1009,21 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (ret) return ret; + ret = adv7511_init_regulators(adv7511); + if (ret) { + dev_err(dev, "failed to init regulators\n"); + return ret; + } + /* * The power down GPIO is optional. If present, toggle it from active to * inactive to wake up the encoder. */ adv7511->gpio_pd = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_HIGH); - if (IS_ERR(adv7511->gpio_pd)) - return PTR_ERR(adv7511->gpio_pd); + if (IS_ERR(adv7511->gpio_pd)) { + ret = PTR_ERR(adv7511->gpio_pd); + goto uninit_regulators; + } if (adv7511->gpio_pd) { mdelay(5); @@ -970,12 +1031,14 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) } adv7511->regmap = devm_regmap_init_i2c(i2c, &adv7511_regmap_config); - if (IS_ERR(adv7511->regmap)) - return PTR_ERR(adv7511->regmap); + if (IS_ERR(adv7511->regmap)) { + ret = PTR_ERR(adv7511->regmap); + goto uninit_regulators; + } ret = regmap_read(adv7511->regmap, ADV7511_REG_CHIP_REVISION, &val); if (ret) - return ret; + goto uninit_regulators; dev_dbg(dev, "Rev. %d\n", val); if (adv7511->type == ADV7511) @@ -985,7 +1048,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) else ret = adv7533_patch_registers(adv7511); if (ret) - return ret; + goto uninit_regulators; regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr); regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR, @@ -995,10 +1058,11 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) adv7511_packet_disable(adv7511, 0xffff); - adv7511->i2c_main = i2c; adv7511->i2c_edid = i2c_new_dummy(i2c->adapter, edid_i2c_addr >> 1); - if (!adv7511->i2c_edid) - return -ENOMEM; + if (!adv7511->i2c_edid) { + ret = -ENOMEM; + goto uninit_regulators; + } if (adv7511->type == ADV7533) { ret = adv7533_init_cec(adv7511); @@ -1045,6 +1109,8 @@ err_unregister_cec: adv7533_uninit_cec(adv7511); err_i2c_unregister_edid: i2c_unregister_device(adv7511->i2c_edid); +uninit_regulators: + adv7511_uninit_regulators(adv7511); return ret; } @@ -1058,6 +1124,8 @@ static int adv7511_remove(struct i2c_client *i2c) adv7533_uninit_cec(adv7511); } + adv7511_uninit_regulators(adv7511); + drm_bridge_remove(&adv7511->bridge); adv7511_audio_exit(adv7511); -- cgit v1.2.1 From cc7e96232763ff33418b088b436a564441347b15 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:28:51 +0200 Subject: drm: bridge: dw-hdmi: Merge __hdmi_phy_i2c_write and hdmi_phy_i2c_write The latter is just an int wrapper around the former void function that unconditionally returns 0. As the return value is never checked, merge the two functions into one. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-2-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index f5009ae39b89..a6685502571e 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -868,7 +868,7 @@ static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec) return true; } -static void __hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, +static void hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, unsigned char addr) { hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); @@ -882,13 +882,6 @@ static void __hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, hdmi_phy_wait_i2c_done(hdmi, 1000); } -static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, - unsigned char addr) -{ - __hdmi_phy_i2c_write(hdmi, data, addr); - return 0; -} - static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable) { hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0, -- cgit v1.2.1 From ecaa98f1e6f7ed3f79def1861f21ff2eac82b8e9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:28:52 +0200 Subject: drm: bridge: dw-hdmi: Remove unneeded arguments to bind/unbind functions The master argument isn't used. The data argument, a void pointer, is used by the bind function only where it's cast to a drm_device pointer, which can easily be obtained from the encoder argument instead. Remove them. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-3-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index a6685502571e..f86894622070 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -1854,12 +1854,10 @@ static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi) return 0; } -int dw_hdmi_bind(struct device *dev, struct device *master, - void *data, struct drm_encoder *encoder, +int dw_hdmi_bind(struct device *dev, struct drm_encoder *encoder, struct resource *iores, int irq, const struct dw_hdmi_plat_data *plat_data) { - struct drm_device *drm = data; struct device_node *np = dev->of_node; struct platform_device_info pdevinfo; struct device_node *ddc_node; @@ -1992,7 +1990,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master, if (ret) goto err_iahb; - ret = dw_hdmi_register(drm, hdmi); + ret = dw_hdmi_register(encoder->dev, hdmi); if (ret) goto err_iahb; @@ -2059,7 +2057,7 @@ err_res: } EXPORT_SYMBOL_GPL(dw_hdmi_bind); -void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) +void dw_hdmi_unbind(struct device *dev) { struct dw_hdmi *hdmi = dev_get_drvdata(dev); -- cgit v1.2.1 From dfa73065d61b6ce57aed90bb0d745c4b6f5b71e7 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Tue, 17 Jan 2017 10:28:53 +0200 Subject: drm: bridge: dw-hdmi: Remove unused function parameter The 'prep' parameter passed to hdmi_phy_configure() is useless. It is hardcoded as 0, and if set, simply prevents the configure function from executing. Remove it. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-4-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index f86894622070..5f8044a7d602 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -931,7 +931,7 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable) HDMI_PHY_CONF0_SELDIPIF_MASK); } -static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, +static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char res, int cscon) { unsigned res_idx; @@ -941,9 +941,6 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; const struct dw_hdmi_phy_config *phy_config = pdata->phy_config; - if (prep) - return -EINVAL; - switch (res) { case 0: /* color resolution 0 is 8 bit colour depth */ case 8: @@ -1072,7 +1069,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi) dw_hdmi_phy_enable_powerdown(hdmi, true); /* Enable CSC */ - ret = hdmi_phy_configure(hdmi, 0, 8, cscon); + ret = hdmi_phy_configure(hdmi, 8, cscon); if (ret) return ret; } -- cgit v1.2.1 From 70c963ec4f15a13197524611875168f23acc4a97 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:28:54 +0200 Subject: drm: bridge: dw-hdmi: Embed drm_bridge in struct dw_hdmi The drm_bridge instance is always needed, there's no point in allocating it separately. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-5-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 5f8044a7d602..2c85b6c07a80 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -116,7 +116,7 @@ struct dw_hdmi_i2c { struct dw_hdmi { struct drm_connector connector; struct drm_encoder *encoder; - struct drm_bridge *bridge; + struct drm_bridge bridge; struct platform_device *audio; enum dw_hdmi_devtype dev_type; @@ -1806,7 +1806,7 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) if (intr_stat & HDMI_IH_PHY_STAT0_HPD) { dev_dbg(hdmi->dev, "EVENT=%s\n", phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout"); - drm_helper_hpd_irq_event(hdmi->bridge->dev); + drm_helper_hpd_irq_event(hdmi->bridge.dev); } hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); @@ -1819,16 +1819,9 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi) { struct drm_encoder *encoder = hdmi->encoder; - struct drm_bridge *bridge; + struct drm_bridge *bridge = &hdmi->bridge; int ret; - bridge = devm_kzalloc(drm->dev, sizeof(*bridge), GFP_KERNEL); - if (!bridge) { - DRM_ERROR("Failed to allocate drm bridge\n"); - return -ENOMEM; - } - - hdmi->bridge = bridge; bridge->driver_private = hdmi; bridge->funcs = &dw_hdmi_bridge_funcs; ret = drm_bridge_attach(encoder, bridge, NULL); -- cgit v1.2.1 From 527b863f6ad4f4f1707cbc2237df9d83d0c848c1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:28:55 +0200 Subject: drm: bridge: dw-hdmi: Remove encoder field from struct dw_hdmi The field isn't needed, remove it. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-6-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 2c85b6c07a80..ef10bb866b2f 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -115,7 +115,6 @@ struct dw_hdmi_i2c { struct dw_hdmi { struct drm_connector connector; - struct drm_encoder *encoder; struct drm_bridge bridge; struct platform_device *audio; @@ -1816,9 +1815,8 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi) +static int dw_hdmi_register(struct drm_encoder *encoder, struct dw_hdmi *hdmi) { - struct drm_encoder *encoder = hdmi->encoder; struct drm_bridge *bridge = &hdmi->bridge; int ret; @@ -1835,7 +1833,7 @@ static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi) drm_connector_helper_add(&hdmi->connector, &dw_hdmi_connector_helper_funcs); - drm_connector_init(drm, &hdmi->connector, + drm_connector_init(encoder->dev, &hdmi->connector, &dw_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); @@ -1867,7 +1865,6 @@ int dw_hdmi_bind(struct device *dev, struct drm_encoder *encoder, hdmi->dev = dev; hdmi->dev_type = plat_data->dev_type; hdmi->sample_rate = 48000; - hdmi->encoder = encoder; hdmi->disabled = true; hdmi->rxsense = true; hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE); @@ -1980,7 +1977,7 @@ int dw_hdmi_bind(struct device *dev, struct drm_encoder *encoder, if (ret) goto err_iahb; - ret = dw_hdmi_register(encoder->dev, hdmi); + ret = dw_hdmi_register(encoder, hdmi); if (ret) goto err_iahb; -- cgit v1.2.1 From ba5d7e6160b7aed4df92d1764aa90790db0e7996 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:28:56 +0200 Subject: drm: bridge: dw-hdmi: Don't forward HPD events to DRM core before attach Hotplug events should only be forwarded to the DRM core by the interrupt handler when the bridge has been attached, otherwise the DRM device pointer will be NULL, resulting in a crash. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-7-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index ef10bb866b2f..488dc1a9204f 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -1805,7 +1805,8 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) if (intr_stat & HDMI_IH_PHY_STAT0_HPD) { dev_dbg(hdmi->dev, "EVENT=%s\n", phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout"); - drm_helper_hpd_irq_event(hdmi->bridge.dev); + if (hdmi->bridge.dev) + drm_helper_hpd_irq_event(hdmi->bridge.dev); } hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); -- cgit v1.2.1 From c608119dfdde9710e4bd068d632beb68bb3517db Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:28:57 +0200 Subject: drm: bridge: dw-hdmi: Move IRQ and IO resource allocation to common code There's no need to duplicate identical code in multiple drivers (two at the moment, one more to come soon). Move it to the dw-hdmi core where it can be shared. If resource allocation ever becomes device-specific later we'll always have the option of splitting it out again. While it at pass the platform device to the bind function to avoid having to cast struct device to struct platform_device. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-8-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 488dc1a9204f..563cbec47da6 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -1843,14 +1843,16 @@ static int dw_hdmi_register(struct drm_encoder *encoder, struct dw_hdmi *hdmi) return 0; } -int dw_hdmi_bind(struct device *dev, struct drm_encoder *encoder, - struct resource *iores, int irq, +int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, const struct dw_hdmi_plat_data *plat_data) { + struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct platform_device_info pdevinfo; struct device_node *ddc_node; struct dw_hdmi *hdmi; + struct resource *iores; + int irq; int ret; u32 val = 1; u8 config0; @@ -1903,6 +1905,7 @@ int dw_hdmi_bind(struct device *dev, struct drm_encoder *encoder, dev_dbg(hdmi->dev, "no ddc property found\n"); } + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); hdmi->regs = devm_ioremap_resource(dev, iores); if (IS_ERR(hdmi->regs)) { ret = PTR_ERR(hdmi->regs); @@ -1945,6 +1948,10 @@ int dw_hdmi_bind(struct device *dev, struct drm_encoder *encoder, initialize_hdmi_ih_mutes(hdmi); + irq = platform_get_irq(pdev, 0); + if (irq < 0) + goto err_iahb; + ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq, dw_hdmi_irq, IRQF_SHARED, dev_name(dev), hdmi); @@ -2025,7 +2032,7 @@ int dw_hdmi_bind(struct device *dev, struct drm_encoder *encoder, if (hdmi->i2c) dw_hdmi_i2c_init(hdmi); - dev_set_drvdata(dev, hdmi); + platform_set_drvdata(pdev, hdmi); return 0; -- cgit v1.2.1 From fd30b38c27c305fcb522bfa7911de241ee1799b5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:28:58 +0200 Subject: drm: bridge: dw-hdmi: Reorder functions to prepare for next commit The next commit will reference structures and functions in a way that currently requires forward declarations. Reorder the functions to avoid that. No functional change to the code is performed. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-9-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 72 ++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 36 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 563cbec47da6..92ce9e532603 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -1575,42 +1575,6 @@ static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi) hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); } -static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge, - struct drm_display_mode *orig_mode, - struct drm_display_mode *mode) -{ - struct dw_hdmi *hdmi = bridge->driver_private; - - mutex_lock(&hdmi->mutex); - - /* Store the display mode for plugin/DKMS poweron events */ - memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); - - mutex_unlock(&hdmi->mutex); -} - -static void dw_hdmi_bridge_disable(struct drm_bridge *bridge) -{ - struct dw_hdmi *hdmi = bridge->driver_private; - - mutex_lock(&hdmi->mutex); - hdmi->disabled = true; - dw_hdmi_update_power(hdmi); - dw_hdmi_update_phy_mask(hdmi); - mutex_unlock(&hdmi->mutex); -} - -static void dw_hdmi_bridge_enable(struct drm_bridge *bridge) -{ - struct dw_hdmi *hdmi = bridge->driver_private; - - mutex_lock(&hdmi->mutex); - hdmi->disabled = false; - dw_hdmi_update_power(hdmi); - dw_hdmi_update_phy_mask(hdmi); - mutex_unlock(&hdmi->mutex); -} - static enum drm_connector_status dw_hdmi_connector_detect(struct drm_connector *connector, bool force) { @@ -1703,6 +1667,42 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = .best_encoder = drm_atomic_helper_best_encoder, }; +static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *orig_mode, + struct drm_display_mode *mode) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + mutex_lock(&hdmi->mutex); + + /* Store the display mode for plugin/DKMS poweron events */ + memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); + + mutex_unlock(&hdmi->mutex); +} + +static void dw_hdmi_bridge_disable(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + mutex_lock(&hdmi->mutex); + hdmi->disabled = true; + dw_hdmi_update_power(hdmi); + dw_hdmi_update_phy_mask(hdmi); + mutex_unlock(&hdmi->mutex); +} + +static void dw_hdmi_bridge_enable(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + mutex_lock(&hdmi->mutex); + hdmi->disabled = false; + dw_hdmi_update_power(hdmi); + dw_hdmi_update_phy_mask(hdmi); + mutex_unlock(&hdmi->mutex); +} + static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { .enable = dw_hdmi_bridge_enable, .disable = dw_hdmi_bridge_disable, -- cgit v1.2.1 From d2ae94ae840bd0b347e417e88b1637df95d499ac Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:28:59 +0200 Subject: drm: bridge: dw-hdmi: Create connector in the bridge attach operation The DRM device is not guaranteed by the bridge API to be available before the attach callback. The driver performs properly at the moment as it doesn't use the drm_bridge_add() registration method. As this will be changed later, move connector creation to attach time to ensure compatibility with the API. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-10-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 92ce9e532603..88cd40adb977 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -1667,6 +1667,25 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = .best_encoder = drm_atomic_helper_best_encoder, }; +static int dw_hdmi_bridge_attach(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + struct drm_encoder *encoder = bridge->encoder; + struct drm_connector *connector = &hdmi->connector; + + connector->interlace_allowed = 1; + connector->polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs); + + drm_connector_init(bridge->dev, connector, &dw_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + + drm_mode_connector_attach_encoder(connector, encoder); + + return 0; +} + static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge, struct drm_display_mode *orig_mode, struct drm_display_mode *mode) @@ -1704,6 +1723,7 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge) } static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { + .attach = dw_hdmi_bridge_attach, .enable = dw_hdmi_bridge_enable, .disable = dw_hdmi_bridge_disable, .mode_set = dw_hdmi_bridge_mode_set, @@ -1829,17 +1849,6 @@ static int dw_hdmi_register(struct drm_encoder *encoder, struct dw_hdmi *hdmi) return -EINVAL; } - hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; - - drm_connector_helper_add(&hdmi->connector, - &dw_hdmi_connector_helper_funcs); - - drm_connector_init(encoder->dev, &hdmi->connector, - &dw_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); - - drm_mode_connector_attach_encoder(&hdmi->connector, encoder); - return 0; } @@ -1862,8 +1871,6 @@ int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, if (!hdmi) return -ENOMEM; - hdmi->connector.interlace_allowed = 1; - hdmi->plat_data = plat_data; hdmi->dev = dev; hdmi->dev_type = plat_data->dev_type; -- cgit v1.2.1 From 69497eb9234eb34994b9a0d2f2c17c4c09f2e969 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:29:00 +0200 Subject: drm: bridge: dw-hdmi: Implement DRM bridge registration As an option for drivers not based on the component framework, register the bridge with the DRM core with the DRM bridge API. Existing drivers based on dw_hdmi_bind() and dw_hdmi_unbind() are not affected as those functions are preserved with their current behaviour. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-11-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 112 ++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 32 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 88cd40adb977..107fea49c4c6 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -1836,24 +1836,9 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int dw_hdmi_register(struct drm_encoder *encoder, struct dw_hdmi *hdmi) -{ - struct drm_bridge *bridge = &hdmi->bridge; - int ret; - - bridge->driver_private = hdmi; - bridge->funcs = &dw_hdmi_bridge_funcs; - ret = drm_bridge_attach(encoder, bridge, NULL); - if (ret) { - DRM_ERROR("Failed to initialize bridge with drm\n"); - return -EINVAL; - } - - return 0; -} - -int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, - const struct dw_hdmi_plat_data *plat_data) +static struct dw_hdmi * +__dw_hdmi_probe(struct platform_device *pdev, + const struct dw_hdmi_plat_data *plat_data) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; @@ -1869,7 +1854,7 @@ int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) - return -ENOMEM; + return ERR_PTR(-ENOMEM); hdmi->plat_data = plat_data; hdmi->dev = dev; @@ -1896,7 +1881,7 @@ int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, break; default: dev_err(dev, "reg-io-width must be 1 or 4\n"); - return -EINVAL; + return ERR_PTR(-EINVAL); } ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); @@ -1905,7 +1890,7 @@ int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, of_node_put(ddc_node); if (!hdmi->ddc) { dev_dbg(hdmi->dev, "failed to read ddc node\n"); - return -EPROBE_DEFER; + return ERR_PTR(-EPROBE_DEFER); } } else { @@ -1956,8 +1941,10 @@ int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, initialize_hdmi_ih_mutes(hdmi); irq = platform_get_irq(pdev, 0); - if (irq < 0) + if (irq < 0) { + ret = irq; goto err_iahb; + } ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq, dw_hdmi_irq, IRQF_SHARED, @@ -1988,11 +1975,11 @@ int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, HDMI_IH_PHY_STAT0); - ret = dw_hdmi_fb_registered(hdmi); - if (ret) - goto err_iahb; + hdmi->bridge.driver_private = hdmi; + hdmi->bridge.funcs = &dw_hdmi_bridge_funcs; + hdmi->bridge.of_node = pdev->dev.of_node; - ret = dw_hdmi_register(encoder, hdmi); + ret = dw_hdmi_fb_registered(hdmi); if (ret) goto err_iahb; @@ -2041,7 +2028,7 @@ int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, platform_set_drvdata(pdev, hdmi); - return 0; + return hdmi; err_iahb: if (hdmi->i2c) { @@ -2055,14 +2042,11 @@ err_isfr: err_res: i2c_put_adapter(hdmi->ddc); - return ret; + return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(dw_hdmi_bind); -void dw_hdmi_unbind(struct device *dev) +static void __dw_hdmi_remove(struct dw_hdmi *hdmi) { - struct dw_hdmi *hdmi = dev_get_drvdata(dev); - if (hdmi->audio && !IS_ERR(hdmi->audio)) platform_device_unregister(hdmi->audio); @@ -2077,6 +2061,70 @@ void dw_hdmi_unbind(struct device *dev) else i2c_put_adapter(hdmi->ddc); } + +/* ----------------------------------------------------------------------------- + * Probe/remove API, used from platforms based on the DRM bridge API. + */ +int dw_hdmi_probe(struct platform_device *pdev, + const struct dw_hdmi_plat_data *plat_data) +{ + struct dw_hdmi *hdmi; + int ret; + + hdmi = __dw_hdmi_probe(pdev, plat_data); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); + + ret = drm_bridge_add(&hdmi->bridge); + if (ret < 0) { + __dw_hdmi_remove(hdmi); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(dw_hdmi_probe); + +void dw_hdmi_remove(struct platform_device *pdev) +{ + struct dw_hdmi *hdmi = platform_get_drvdata(pdev); + + drm_bridge_remove(&hdmi->bridge); + + __dw_hdmi_remove(hdmi); +} +EXPORT_SYMBOL_GPL(dw_hdmi_remove); + +/* ----------------------------------------------------------------------------- + * Bind/unbind API, used from platforms based on the component framework. + */ +int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, + const struct dw_hdmi_plat_data *plat_data) +{ + struct dw_hdmi *hdmi; + int ret; + + hdmi = __dw_hdmi_probe(pdev, plat_data); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); + + ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL); + if (ret) { + dw_hdmi_remove(pdev); + DRM_ERROR("Failed to initialize bridge with drm\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(dw_hdmi_bind); + +void dw_hdmi_unbind(struct device *dev) +{ + struct dw_hdmi *hdmi = dev_get_drvdata(dev); + + __dw_hdmi_remove(hdmi); +} EXPORT_SYMBOL_GPL(dw_hdmi_unbind); MODULE_AUTHOR("Sascha Hauer "); -- cgit v1.2.1 From 1acc6bdeee1ef2ecac3ba070a403827ab8f16be5 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Tue, 17 Jan 2017 10:29:01 +0200 Subject: drm: bridge: dw-hdmi: Remove PHY configuration resolution parameter The current code hard codes the call of hdmi_phy_configure() to be 8bpp and provides extraneous error checking to verify that this hardcoded value is correct. Simplify the implementation by removing the argument. Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-12-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 107fea49c4c6..b4fb0bd78910 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -930,31 +930,14 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable) HDMI_PHY_CONF0_SELDIPIF_MASK); } -static int hdmi_phy_configure(struct dw_hdmi *hdmi, - unsigned char res, int cscon) +static int hdmi_phy_configure(struct dw_hdmi *hdmi, int cscon) { - unsigned res_idx; u8 val, msec; const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg; const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; const struct dw_hdmi_phy_config *phy_config = pdata->phy_config; - switch (res) { - case 0: /* color resolution 0 is 8 bit colour depth */ - case 8: - res_idx = DW_HDMI_RES_8; - break; - case 10: - res_idx = DW_HDMI_RES_10; - break; - case 12: - res_idx = DW_HDMI_RES_12; - break; - default: - return -EINVAL; - } - /* PLL/MPLL Cfg - always match on final entry */ for (; mpll_config->mpixelclock != ~0UL; mpll_config++) if (hdmi->hdmi_data.video_mode.mpixelclock <= @@ -1004,11 +987,11 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, HDMI_PHY_I2CM_SLAVE_ADDR); hdmi_phy_test_clear(hdmi, 0); - hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].cpce, 0x06); - hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].gmp, 0x15); + hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce, 0x06); + hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp, 0x15); /* CURRCTRL */ - hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[res_idx], 0x10); + hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0], 0x10); hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); @@ -1068,7 +1051,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi) dw_hdmi_phy_enable_powerdown(hdmi, true); /* Enable CSC */ - ret = hdmi_phy_configure(hdmi, 8, cscon); + ret = hdmi_phy_configure(hdmi, cscon); if (ret) return ret; } -- cgit v1.2.1 From f4104e8fe12c173fbba5e7e30b846e09eeb5bfbd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:29:02 +0200 Subject: drm: bridge: dw-hdmi: Rename CONF0 SPARECTRL bit to SVSRET The bit is documented in a Rockchip BSP as #define m_SVSRET_SIG (1 << 5) /* depend on PHY_MHL_COMB0=1 */ This is confirmed by a Renesas platform, which uses a 2.0 DWC HDMI TX as the RK3288. Rename the bit accordingly. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-13-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 8 ++++---- drivers/gpu/drm/bridge/dw-hdmi.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index b4fb0bd78910..06c252f560ad 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -895,11 +895,11 @@ static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable) HDMI_PHY_CONF0_ENTMDS_MASK); } -static void dw_hdmi_phy_enable_spare(struct dw_hdmi *hdmi, u8 enable) +static void dw_hdmi_phy_enable_svsret(struct dw_hdmi *hdmi, u8 enable) { hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, - HDMI_PHY_CONF0_SPARECTRL_OFFSET, - HDMI_PHY_CONF0_SPARECTRL_MASK); + HDMI_PHY_CONF0_SVSRET_OFFSET, + HDMI_PHY_CONF0_SVSRET_MASK); } static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable) @@ -1014,7 +1014,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, int cscon) dw_hdmi_phy_gen2_pddq(hdmi, 0); if (hdmi->dev_type == RK3288_HDMI) - dw_hdmi_phy_enable_spare(hdmi, 1); + dw_hdmi_phy_enable_svsret(hdmi, 1); /*Wait for PHY PLL lock */ msec = 5; diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h index 55135bbd0c16..08235aef2fa3 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/dw-hdmi.h @@ -847,8 +847,8 @@ enum { HDMI_PHY_CONF0_PDZ_OFFSET = 7, HDMI_PHY_CONF0_ENTMDS_MASK = 0x40, HDMI_PHY_CONF0_ENTMDS_OFFSET = 6, - HDMI_PHY_CONF0_SPARECTRL_MASK = 0x20, - HDMI_PHY_CONF0_SPARECTRL_OFFSET = 5, + HDMI_PHY_CONF0_SVSRET_MASK = 0x20, + HDMI_PHY_CONF0_SVSRET_OFFSET = 5, HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10, HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4, HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8, -- cgit v1.2.1 From 0527e12e8264ae96b1fcc550b4a9e5940f4ffc30 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:29:03 +0200 Subject: drm: bridge: dw-hdmi: Reject invalid product IDs The DWC HDMI TX can be recognized by the two product identification registers. If the registers don't read as expect the IP will be very different than what the driver has been designed for, or will be misconfigured in a way that makes it non-operational (invalid memory address, incorrect clocks, ...). We should reject this situation with an error. While this isn't critical for proper operation with supported IPs at the moment, the driver will soon gain automatic device-specific handling based on runtime device identification. This change makes it easier to implement that without having to default to a random guess in case the device can't be identified. While at it print a readable version number in the device identification message instead of raw register values. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-14-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 25 +++++++++++++++++++------ drivers/gpu/drm/bridge/dw-hdmi.h | 8 ++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 06c252f560ad..1809247745b8 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -1832,6 +1832,9 @@ __dw_hdmi_probe(struct platform_device *pdev, int irq; int ret; u32 val = 1; + u16 version; + u8 prod_id0; + u8 prod_id1; u8 config0; u8 config1; @@ -1914,12 +1917,22 @@ __dw_hdmi_probe(struct platform_device *pdev, } /* Product and revision IDs */ - dev_info(dev, - "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n", - hdmi_readb(hdmi, HDMI_DESIGN_ID), - hdmi_readb(hdmi, HDMI_REVISION_ID), - hdmi_readb(hdmi, HDMI_PRODUCT_ID0), - hdmi_readb(hdmi, HDMI_PRODUCT_ID1)); + version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8) + | (hdmi_readb(hdmi, HDMI_REVISION_ID) << 0); + prod_id0 = hdmi_readb(hdmi, HDMI_PRODUCT_ID0); + prod_id1 = hdmi_readb(hdmi, HDMI_PRODUCT_ID1); + + if (prod_id0 != HDMI_PRODUCT_ID0_HDMI_TX || + (prod_id1 & ~HDMI_PRODUCT_ID1_HDCP) != HDMI_PRODUCT_ID1_HDMI_TX) { + dev_err(dev, "Unsupported HDMI controller (%04x:%02x:%02x)\n", + version, prod_id0, prod_id1); + ret = -ENODEV; + goto err_iahb; + } + + dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP\n", + version >> 12, version & 0xfff, + prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without"); initialize_hdmi_ih_mutes(hdmi); diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h index 08235aef2fa3..91d7fabbd6e5 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/dw-hdmi.h @@ -545,6 +545,14 @@ #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12 enum { +/* PRODUCT_ID0 field values */ + HDMI_PRODUCT_ID0_HDMI_TX = 0xa0, + +/* PRODUCT_ID1 field values */ + HDMI_PRODUCT_ID1_HDCP = 0xc0, + HDMI_PRODUCT_ID1_HDMI_RX = 0x02, + HDMI_PRODUCT_ID1_HDMI_TX = 0x01, + /* CONFIG0_ID field values */ HDMI_CONFIG0_I2S = 0x10, -- cgit v1.2.1 From 0c674948b7f4e4ffc19ba5af65a274e945c0c689 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:29:04 +0200 Subject: drm: bridge: dw-hdmi: Detect AHB audio DMA using correct register Bit 0 in CONFIG1_ID tells whether the IP core uses an AHB slave interface for control. The correct way to identify AHB audio DMA support is through bit 1 in CONFIG3_ID. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-15-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 6 +++--- drivers/gpu/drm/bridge/dw-hdmi.h | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 1809247745b8..730a7558d4d4 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -1836,7 +1836,7 @@ __dw_hdmi_probe(struct platform_device *pdev, u8 prod_id0; u8 prod_id1; u8 config0; - u8 config1; + u8 config3; hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) @@ -1988,9 +1988,9 @@ __dw_hdmi_probe(struct platform_device *pdev, pdevinfo.id = PLATFORM_DEVID_AUTO; config0 = hdmi_readb(hdmi, HDMI_CONFIG0_ID); - config1 = hdmi_readb(hdmi, HDMI_CONFIG1_ID); + config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID); - if (config1 & HDMI_CONFIG1_AHB) { + if (config3 & HDMI_CONFIG3_AHBAUDDMA) { struct dw_hdmi_audio_data audio; audio.phys = iores->start; diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h index 91d7fabbd6e5..a4fd64a203c9 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/dw-hdmi.h @@ -559,6 +559,10 @@ enum { /* CONFIG1_ID field values */ HDMI_CONFIG1_AHB = 0x01, +/* CONFIG3_ID field values */ + HDMI_CONFIG3_AHBAUDDMA = 0x02, + HDMI_CONFIG3_GPAUD = 0x01, + /* IH_FC_INT2 field values */ HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03, HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02, -- cgit v1.2.1 From be41fc55f1aa3c9ae0eb9e0b384db5150eca055f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:29:05 +0200 Subject: drm: bridge: dw-hdmi: Handle overflow workaround based on device version Use the device version queried at runtime instead of the device type provided through platform data to handle the overflow workaround. This will make support of other SoCs integrating the same HDMI TX controller version easier. Among the supported platforms only i.MX6DL and i.MX6Q have been identified as needing the workaround. Disabling it on Rockchip RK3288 (which integrates a v2.00a controller) didn't produce any error or artifact. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-16-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 46 ++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 730a7558d4d4..f4faa14213e5 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -117,8 +117,10 @@ struct dw_hdmi { struct drm_connector connector; struct drm_bridge bridge; - struct platform_device *audio; enum dw_hdmi_devtype dev_type; + unsigned int version; + + struct platform_device *audio; struct device *dev; struct clk *isfr_clk; struct clk *iahb_clk; @@ -1323,19 +1325,38 @@ static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi) /* Workaround to clear the overflow condition */ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) { - int count; + unsigned int count; + unsigned int i; u8 val; - /* TMDS software reset */ - hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); + /* + * Under some circumstances the Frame Composer arithmetic unit can miss + * an FC register write due to being busy processing the previous one. + * The issue can be worked around by issuing a TMDS software reset and + * then write one of the FC registers several times. + * + * The number of iterations matters and depends on the HDMI TX revision + * (and possibly on the platform). So far only i.MX6Q (v1.30a) and + * i.MX6DL (v1.31a) have been identified as needing the workaround, with + * 4 and 1 iterations respectively. + */ - val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF); - if (hdmi->dev_type == IMX6DL_HDMI) { - hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); + switch (hdmi->version) { + case 0x130a: + count = 4; + break; + case 0x131a: + count = 1; + break; + default: return; } - for (count = 0; count < 4; count++) + /* TMDS software reset */ + hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); + + val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF); + for (i = 0; i < count; i++) hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); } @@ -1832,7 +1853,6 @@ __dw_hdmi_probe(struct platform_device *pdev, int irq; int ret; u32 val = 1; - u16 version; u8 prod_id0; u8 prod_id1; u8 config0; @@ -1917,21 +1937,21 @@ __dw_hdmi_probe(struct platform_device *pdev, } /* Product and revision IDs */ - version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8) - | (hdmi_readb(hdmi, HDMI_REVISION_ID) << 0); + hdmi->version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8) + | (hdmi_readb(hdmi, HDMI_REVISION_ID) << 0); prod_id0 = hdmi_readb(hdmi, HDMI_PRODUCT_ID0); prod_id1 = hdmi_readb(hdmi, HDMI_PRODUCT_ID1); if (prod_id0 != HDMI_PRODUCT_ID0_HDMI_TX || (prod_id1 & ~HDMI_PRODUCT_ID1_HDCP) != HDMI_PRODUCT_ID1_HDMI_TX) { dev_err(dev, "Unsupported HDMI controller (%04x:%02x:%02x)\n", - version, prod_id0, prod_id1); + hdmi->version, prod_id0, prod_id1); ret = -ENODEV; goto err_iahb; } dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP\n", - version >> 12, version & 0xfff, + hdmi->version >> 12, hdmi->version & 0xfff, prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without"); initialize_hdmi_ih_mutes(hdmi); -- cgit v1.2.1 From faba6c3cff177689aec132291b1cf537831d9a2e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:29:06 +0200 Subject: drm: bridge: dw-hdmi: Detect PHY type at runtime Detect the PHY type and use it to handle the PHY type-specific SVSRET signal. Signed-off-by: Laurent Pinchart Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-17-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 68 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index f4faa14213e5..ef4f2f96ed2c 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -113,6 +113,12 @@ struct dw_hdmi_i2c { bool is_regaddr; }; +struct dw_hdmi_phy_data { + enum dw_hdmi_phy_type type; + const char *name; + bool has_svsret; +}; + struct dw_hdmi { struct drm_connector connector; struct drm_bridge bridge; @@ -134,7 +140,9 @@ struct dw_hdmi { u8 edid[HDMI_EDID_LEN]; bool cable_plugin; + const struct dw_hdmi_phy_data *phy; bool phy_enabled; + struct drm_display_mode previous_mode; struct i2c_adapter *ddc; @@ -1015,7 +1023,8 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, int cscon) dw_hdmi_phy_gen2_txpwron(hdmi, 1); dw_hdmi_phy_gen2_pddq(hdmi, 0); - if (hdmi->dev_type == RK3288_HDMI) + /* The DWC MHL and HDMI 2.0 PHYs need the SVSRET signal to be set. */ + if (hdmi->phy->has_svsret) dw_hdmi_phy_enable_svsret(hdmi, 1); /*Wait for PHY PLL lock */ @@ -1840,6 +1849,54 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static const struct dw_hdmi_phy_data dw_hdmi_phys[] = { + { + .type = DW_HDMI_PHY_DWC_HDMI_TX_PHY, + .name = "DWC HDMI TX PHY", + }, { + .type = DW_HDMI_PHY_DWC_MHL_PHY_HEAC, + .name = "DWC MHL PHY + HEAC PHY", + .has_svsret = true, + }, { + .type = DW_HDMI_PHY_DWC_MHL_PHY, + .name = "DWC MHL PHY", + .has_svsret = true, + }, { + .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC, + .name = "DWC HDMI 3D TX PHY + HEAC PHY", + }, { + .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY, + .name = "DWC HDMI 3D TX PHY", + }, { + .type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY, + .name = "DWC HDMI 2.0 TX PHY", + .has_svsret = true, + } +}; + +static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) +{ + unsigned int i; + u8 phy_type; + + phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID); + + for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) { + if (dw_hdmi_phys[i].type == phy_type) { + hdmi->phy = &dw_hdmi_phys[i]; + return 0; + } + } + + if (phy_type == DW_HDMI_PHY_VENDOR_PHY) + dev_err(hdmi->dev, "Unsupported vendor HDMI PHY\n"); + else + dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n", + phy_type); + + return -ENODEV; +} + static struct dw_hdmi * __dw_hdmi_probe(struct platform_device *pdev, const struct dw_hdmi_plat_data *plat_data) @@ -1950,9 +2007,14 @@ __dw_hdmi_probe(struct platform_device *pdev, goto err_iahb; } - dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP\n", + ret = dw_hdmi_detect_phy(hdmi); + if (ret < 0) + goto err_iahb; + + dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP (%s)\n", hdmi->version >> 12, hdmi->version & 0xfff, - prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without"); + prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without", + hdmi->phy->name); initialize_hdmi_ih_mutes(hdmi); -- cgit v1.2.1 From f0e7f2f3b6333a02dd7cb89822e6330631c9a3e3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:29:07 +0200 Subject: drm: bridge: dw-hdmi: Define and use macros for PHY register addresses Replace the hardcoded register address numerical values with macros to clarify the code. This change has been tested by comparing the assembly code before and after the change. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-18-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 35 ++++++++++++--------- drivers/gpu/drm/bridge/dw-hdmi.h | 66 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 15 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index ef4f2f96ed2c..6e605fd910ef 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -997,21 +997,26 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, int cscon) HDMI_PHY_I2CM_SLAVE_ADDR); hdmi_phy_test_clear(hdmi, 0); - hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce, 0x06); - hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp, 0x15); - - /* CURRCTRL */ - hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0], 0x10); - - hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ - hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); - - hdmi_phy_i2c_write(hdmi, phy_config->term, 0x19); /* TXTERM */ - hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 0x09); /* CKSYMTXCTRL */ - hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 0x0E); /* VLEVCTRL */ - - /* REMOVE CLK TERM */ - hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */ + hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce, + HDMI_3D_TX_PHY_CPCE_CTRL); + hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp, + HDMI_3D_TX_PHY_GMPCTRL); + hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0], + HDMI_3D_TX_PHY_CURRCTRL); + + hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL); + hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK, + HDMI_3D_TX_PHY_MSM_CTRL); + + hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM); + hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, + HDMI_3D_TX_PHY_CKSYMTXCTRL); + hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, + HDMI_3D_TX_PHY_VLEVCTRL); + + /* Override and disable clock termination. */ + hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE, + HDMI_3D_TX_PHY_CKCALCTRL); dw_hdmi_phy_enable_powerdown(hdmi, false); diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h index a4fd64a203c9..f3c149c88d71 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/dw-hdmi.h @@ -1085,4 +1085,70 @@ enum { HDMI_I2CM_CTLINT_ARB_MASK = 0x4, }; +/* + * HDMI 3D TX PHY registers + */ +#define HDMI_3D_TX_PHY_PWRCTRL 0x00 +#define HDMI_3D_TX_PHY_SERDIVCTRL 0x01 +#define HDMI_3D_TX_PHY_SERCKCTRL 0x02 +#define HDMI_3D_TX_PHY_SERCKKILLCTRL 0x03 +#define HDMI_3D_TX_PHY_TXRESCTRL 0x04 +#define HDMI_3D_TX_PHY_CKCALCTRL 0x05 +#define HDMI_3D_TX_PHY_CPCE_CTRL 0x06 +#define HDMI_3D_TX_PHY_TXCLKMEASCTRL 0x07 +#define HDMI_3D_TX_PHY_TXMEASCTRL 0x08 +#define HDMI_3D_TX_PHY_CKSYMTXCTRL 0x09 +#define HDMI_3D_TX_PHY_CMPSEQCTRL 0x0a +#define HDMI_3D_TX_PHY_CMPPWRCTRL 0x0b +#define HDMI_3D_TX_PHY_CMPMODECTRL 0x0c +#define HDMI_3D_TX_PHY_MEASCTRL 0x0d +#define HDMI_3D_TX_PHY_VLEVCTRL 0x0e +#define HDMI_3D_TX_PHY_D2ACTRL 0x0f +#define HDMI_3D_TX_PHY_CURRCTRL 0x10 +#define HDMI_3D_TX_PHY_DRVANACTRL 0x11 +#define HDMI_3D_TX_PHY_PLLMEASCTRL 0x12 +#define HDMI_3D_TX_PHY_PLLPHBYCTRL 0x13 +#define HDMI_3D_TX_PHY_GRP_CTRL 0x14 +#define HDMI_3D_TX_PHY_GMPCTRL 0x15 +#define HDMI_3D_TX_PHY_MPLLMEASCTRL 0x16 +#define HDMI_3D_TX_PHY_MSM_CTRL 0x17 +#define HDMI_3D_TX_PHY_SCRPB_STATUS 0x18 +#define HDMI_3D_TX_PHY_TXTERM 0x19 +#define HDMI_3D_TX_PHY_PTRPT_ENBL 0x1a +#define HDMI_3D_TX_PHY_PATTERNGEN 0x1b +#define HDMI_3D_TX_PHY_SDCAP_MODE 0x1c +#define HDMI_3D_TX_PHY_SCOPEMODE 0x1d +#define HDMI_3D_TX_PHY_DIGTXMODE 0x1e +#define HDMI_3D_TX_PHY_STR_STATUS 0x1f +#define HDMI_3D_TX_PHY_SCOPECNT0 0x20 +#define HDMI_3D_TX_PHY_SCOPECNT1 0x21 +#define HDMI_3D_TX_PHY_SCOPECNT2 0x22 +#define HDMI_3D_TX_PHY_SCOPECNTCLK 0x23 +#define HDMI_3D_TX_PHY_SCOPESAMPLE 0x24 +#define HDMI_3D_TX_PHY_SCOPECNTMSB01 0x25 +#define HDMI_3D_TX_PHY_SCOPECNTMSB2CK 0x26 + +/* HDMI_3D_TX_PHY_CKCALCTRL values */ +#define HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE BIT(15) + +/* HDMI_3D_TX_PHY_MSM_CTRL values */ +#define HDMI_3D_TX_PHY_MSM_CTRL_MPLL_PH_SEL_CK BIT(13) +#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_CLK_REF_MPLL (0 << 1) +#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_OFF (1 << 1) +#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_PCLK (2 << 1) +#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK (3 << 1) +#define HDMI_3D_TX_PHY_MSM_CTRL_SCOPE_CK_SEL BIT(0) + +/* HDMI_3D_TX_PHY_PTRPT_ENBL values */ +#define HDMI_3D_TX_PHY_PTRPT_ENBL_OVERRIDE BIT(15) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_PG_SKIP_BIT2 BIT(8) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_PG_SKIP_BIT1 BIT(7) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_PG_SKIP_BIT0 BIT(6) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_CK_REF_ENB BIT(5) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_RCAL_ENB BIT(4) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_TX_CLK_ALIGN_ENB BIT(3) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_TX_READY BIT(2) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_CKO_WORD_ENB BIT(1) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_REFCLK_ENB BIT(0) + #endif /* __DW_HDMI_H__ */ -- cgit v1.2.1 From 54d72737b098f3597c57693e1aa96699a21b11fe Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:29:08 +0200 Subject: drm: bridge: dw-hdmi: Fix the name of the PHY reset macros The PHY reset signal is controlled by bit PHYRSTZ in the MC_PHYRSTZ register. The signal is active low on Gen1 PHYs and active high on Gen2 PHYs. The driver toggles the signal high then low, which is correct for all currently supported platforms, but the register values macros are incorrectly named. Replace them with a single macro named after the bit, and add a comment to the source code to explain the behaviour. The driver's behaviour isn't changed by this rename, the code will still need to be fixed to support Gen1 PHYs. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-19-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 6 +++--- drivers/gpu/drm/bridge/dw-hdmi.h | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 6e605fd910ef..93e8816f1f78 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -986,9 +986,9 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, int cscon) /* gen2 pddq */ dw_hdmi_phy_gen2_pddq(hdmi, 1); - /* PHY reset */ - hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ); - hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ); + /* PHY reset. The reset signal is active high on Gen2 PHYs. */ + hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ); + hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ); hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST); diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h index f3c149c88d71..325b0b8ae639 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/dw-hdmi.h @@ -989,8 +989,7 @@ enum { HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0, /* MC_PHYRSTZ field values */ - HDMI_MC_PHYRSTZ_ASSERT = 0x0, - HDMI_MC_PHYRSTZ_DEASSERT = 0x1, + HDMI_MC_PHYRSTZ_PHYRSTZ = 0x01, /* MC_HEACPHY_RST field values */ HDMI_MC_HEACPHY_RST_ASSERT = 0x1, -- cgit v1.2.1 From 2668db37888ff63282147b00dcf54fa491831df3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 17 Jan 2017 10:29:09 +0200 Subject: drm: bridge: dw-hdmi: Assert SVSRET before resetting the PHY According to the PHY IP core vendor, the SVSRET signal must be asserted before resetting the PHY. Tests on RK3288 and R-Car Gen3 showed no regression, the change should thus be safe. Signed-off-by: Laurent Pinchart Reviewed-by: Jose Abreu Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/20170117082910.27023-20-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/dw-hdmi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 93e8816f1f78..4fda0717e789 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -986,6 +986,10 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, int cscon) /* gen2 pddq */ dw_hdmi_phy_gen2_pddq(hdmi, 1); + /* Leave low power consumption mode by asserting SVSRET. */ + if (hdmi->phy->has_svsret) + dw_hdmi_phy_enable_svsret(hdmi, 1); + /* PHY reset. The reset signal is active high on Gen2 PHYs. */ hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ); hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ); @@ -1028,11 +1032,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, int cscon) dw_hdmi_phy_gen2_txpwron(hdmi, 1); dw_hdmi_phy_gen2_pddq(hdmi, 0); - /* The DWC MHL and HDMI 2.0 PHYs need the SVSRET signal to be set. */ - if (hdmi->phy->has_svsret) - dw_hdmi_phy_enable_svsret(hdmi, 1); - - /*Wait for PHY PLL lock */ + /* Wait for PHY PLL lock */ msec = 5; do { val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; -- cgit v1.2.1 From 518cb7057a59b9441336d2e88a396d52b6ab0cce Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 16 Jan 2017 16:52:47 -0800 Subject: drm/bridge: adv7511: Use work_struct to defer hotplug handing to out of irq context I was recently seeing issues with EDID probing, where the logic to wait for the EDID read bit to be set by the IRQ wasn't happening and the code would time out and fail. Digging deeper, I found this was due to the fact that IRQs were disabled as we were running in IRQ context from the HPD signal. Thus this patch changes the logic to handle the HPD signal via a work_struct so we can be out of irq context. With this patch, the EDID probing on hotplug does not time out. Cc: David Airlie Cc: Archit Taneja Cc: Wolfram Sang Cc: Lars-Peter Clausen Cc: Laurent Pinchart Cc: dri-devel@lists.freedesktop.org Reviewed-by: Laurent Pinchart Tested-by: Laurent Pinchart Signed-off-by: John Stultz Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1484614372-15342-2-git-send-email-john.stultz@linaro.org --- drivers/gpu/drm/bridge/adv7511/adv7511.h | 2 ++ drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 039147fde7b0..fe18a5d2d84b 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -318,6 +318,8 @@ struct adv7511 { bool edid_read; wait_queue_head_t wq; + struct work_struct hpd_work; + struct drm_bridge bridge; struct drm_connector connector; diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 70b426925604..ce5c19c829b9 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -402,6 +402,13 @@ static bool adv7511_hpd(struct adv7511 *adv7511) return false; } +static void adv7511_hpd_work(struct work_struct *work) +{ + struct adv7511 *adv7511 = container_of(work, struct adv7511, hpd_work); + + drm_helper_hpd_irq_event(adv7511->connector.dev); +} + static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) { unsigned int irq0, irq1; @@ -419,7 +426,7 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1); if (process_hpd && irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder) - drm_helper_hpd_irq_event(adv7511->connector.dev); + schedule_work(&adv7511->hpd_work); if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) { adv7511->edid_read = true; @@ -1070,6 +1077,8 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) goto err_i2c_unregister_edid; } + INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work); + if (i2c->irq) { init_waitqueue_head(&adv7511->wq); -- cgit v1.2.1 From 6d5104c5a6b56385426e15047050584794bb6254 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 16 Jan 2017 16:52:48 -0800 Subject: drm/bridge: adv7511: Switch to using drm_kms_helper_hotplug_event() In chasing down a previous issue with EDID probing from calling drm_helper_hpd_irq_event() from irq context, Laurent noticed that the DRM documentation suggests that drm_kms_helper_hotplug_event() should be used instead. Thus this patch replaces drm_helper_hpd_irq_event() with drm_kms_helper_hotplug_event(), which requires we update the connector.status entry and only call _hotplug_event() when the status changes. Cc: David Airlie Cc: Archit Taneja Cc: Wolfram Sang Cc: Lars-Peter Clausen Cc: Laurent Pinchart Cc: dri-devel@lists.freedesktop.org Reviewed-by: Laurent Pinchart Tested-by: Laurent Pinchart Signed-off-by: John Stultz Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1484614372-15342-3-git-send-email-john.stultz@linaro.org --- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index ce5c19c829b9..60773387fcdf 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -405,8 +405,22 @@ static bool adv7511_hpd(struct adv7511 *adv7511) static void adv7511_hpd_work(struct work_struct *work) { struct adv7511 *adv7511 = container_of(work, struct adv7511, hpd_work); + enum drm_connector_status status; + unsigned int val; + int ret; - drm_helper_hpd_irq_event(adv7511->connector.dev); + ret = regmap_read(adv7511->regmap, ADV7511_REG_STATUS, &val); + if (ret < 0) + status = connector_status_disconnected; + else if (val & ADV7511_STATUS_HPD) + status = connector_status_connected; + else + status = connector_status_disconnected; + + if (adv7511->connector.status != status) { + adv7511->connector.status = status; + drm_kms_helper_hotplug_event(adv7511->connector.dev); + } } static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) -- cgit v1.2.1 From 40d86d2d22b04c2b2e48e2fe7054b85cf5021f25 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Mon, 16 Jan 2017 16:52:49 -0800 Subject: drm/bridge: adv7511: Enable HPD interrupts to support hotplug and improve monitor detection On some adv7511 implementations, we can get some spurious disconnect signals which can cause monitor probing to fail. This patch enables HPD (hot plug detect) interrupt support which allows the monitor to be properly re-initialized when the spurious disconnect signal goes away. This also enables proper hotplug support. Cc: David Airlie Cc: Archit Taneja Cc: Wolfram Sang Cc: Lars-Peter Clausen Cc: Laurent Pinchart Cc: dri-devel@lists.freedesktop.org Acked-by: Laurent Pinchart Tested-by: Laurent Pinchart Originally-by: Archit Taneja [jstultz: Added proper commit message] Signed-off-by: John Stultz Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1484614372-15342-4-git-send-email-john.stultz@linaro.org --- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 60773387fcdf..72939d429da6 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -338,7 +338,7 @@ static void adv7511_power_on(struct adv7511 *adv7511) * Still, let's be safe and stick to the documentation. */ regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0), - ADV7511_INT0_EDID_READY); + ADV7511_INT0_EDID_READY | ADV7511_INT0_HPD); regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1), ADV7511_INT1_DDC_ERROR); } @@ -846,6 +846,10 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge) if (adv->type == ADV7533) ret = adv7533_attach_dsi(adv); + if (adv->i2c_main->irq) + regmap_write(adv->regmap, ADV7511_REG_INT_ENABLE(0), + ADV7511_INT0_HPD); + return ret; } -- cgit v1.2.1 From 651e4769ba2a9f20c4b8a823ae2727bf7fa9c9f0 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 16 Jan 2017 16:52:50 -0800 Subject: drm/bridge: adv7511: Rework adv7511_power_on/off() so they can be reused internally In chasing down issues with EDID probing, I found some duplicated but incomplete logic used to power the chip on and off. This patch refactors the adv7511_power_on/off functions, so they can be used for internal needs. Cc: David Airlie Cc: Archit Taneja Cc: Wolfram Sang Cc: Lars-Peter Clausen Cc: Laurent Pinchart Cc: dri-devel@lists.freedesktop.org Signed-off-by: John Stultz Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1484614372-15342-5-git-send-email-john.stultz@linaro.org --- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 72939d429da6..545ceff5890f 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -325,7 +325,7 @@ static void adv7511_set_link_config(struct adv7511 *adv7511, adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB; } -static void adv7511_power_on(struct adv7511 *adv7511) +static void __adv7511_power_on(struct adv7511 *adv7511) { adv7511->current_edid_segment = -1; @@ -354,6 +354,11 @@ static void adv7511_power_on(struct adv7511 *adv7511) regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, ADV7511_REG_POWER2_HPD_SRC_MASK, ADV7511_REG_POWER2_HPD_SRC_NONE); +} + +static void adv7511_power_on(struct adv7511 *adv7511) +{ + __adv7511_power_on(adv7511); /* * Most of the registers are reset during power down or when HPD is low. @@ -362,21 +367,23 @@ static void adv7511_power_on(struct adv7511 *adv7511) if (adv7511->type == ADV7533) adv7533_dsi_power_on(adv7511); - adv7511->powered = true; } -static void adv7511_power_off(struct adv7511 *adv7511) +static void __adv7511_power_off(struct adv7511 *adv7511) { /* TODO: setup additional power down modes */ regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, ADV7511_POWER_POWER_DOWN, ADV7511_POWER_POWER_DOWN); regcache_mark_dirty(adv7511->regmap); +} +static void adv7511_power_off(struct adv7511 *adv7511) +{ + __adv7511_power_off(adv7511); if (adv7511->type == ADV7533) adv7533_dsi_power_off(adv7511); - adv7511->powered = false; } -- cgit v1.2.1 From 4226d9b127cf4758ba0e07931b3f0d59f1b1a50c Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 16 Jan 2017 16:52:51 -0800 Subject: drm/bridge: adv7511: Reuse __adv7511_power_on/off() when probing EDID Thus this patch changes the EDID probing logic so that we re-use the __adv7511_power_on/off() calls instead of duplciating logic. This does change behavior slightly as it adds the HPD signal pulse to the EDID probe path, but Archit has had a patch to add HPD signal pulse to the EDID probe path before, so this should address the cases where that helped. Another difference is that regcache_mark_dirty() is also called in the power off path once EDID is probed. Cc: David Airlie Cc: Archit Taneja Cc: Wolfram Sang Cc: Lars-Peter Clausen Cc: Laurent Pinchart Cc: dri-devel@lists.freedesktop.org Reviewed-by: Laurent Pinchart Tested-by: Laurent Pinchart Signed-off-by: John Stultz Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1484614372-15342-6-git-send-email-john.stultz@linaro.org --- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 545ceff5890f..17b9e98b151b 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -573,24 +573,13 @@ static int adv7511_get_modes(struct adv7511 *adv7511, unsigned int count; /* Reading the EDID only works if the device is powered */ - if (!adv7511->powered) { - regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, - ADV7511_POWER_POWER_DOWN, 0); - if (adv7511->i2c_main->irq) { - regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0), - ADV7511_INT0_EDID_READY); - regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1), - ADV7511_INT1_DDC_ERROR); - } - adv7511->current_edid_segment = -1; - } + if (!adv7511->powered) + __adv7511_power_on(adv7511); edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511); if (!adv7511->powered) - regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, - ADV7511_POWER_POWER_DOWN, - ADV7511_POWER_POWER_DOWN); + __adv7511_power_off(adv7511); kfree(adv7511->edid); adv7511->edid = edid; -- cgit v1.2.1 From 3587c856675c45809010c2cee5b21096f6e8e938 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 16 Jan 2017 16:52:52 -0800 Subject: drm/bridge: adv7511: Re-write the i2c address before EDID probing I've found that by just turning the chip on and off via the POWER_DOWN register, I end up getting i2c_transfer errors on HiKey. Investigating further, it turns out that some of the register state in hardware is getting lost, as the device registers are reset when the chip is powered down. Thus this patch simply re-writes the i2c address to the ADV7511_REG_EDID_I2C_ADDR register to ensure its properly set before we try to read the EDID data. Cc: David Airlie Cc: Archit Taneja Cc: Wolfram Sang Cc: Lars-Peter Clausen Cc: Laurent Pinchart Cc: dri-devel@lists.freedesktop.org Reviewed-by: Laurent Pinchart Tested-by: Laurent Pinchart Signed-off-by: John Stultz Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1484614372-15342-7-git-send-email-john.stultz@linaro.org --- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/bridge') diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 17b9e98b151b..f75ab6278113 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -573,9 +573,17 @@ static int adv7511_get_modes(struct adv7511 *adv7511, unsigned int count; /* Reading the EDID only works if the device is powered */ - if (!adv7511->powered) + if (!adv7511->powered) { + unsigned int edid_i2c_addr = + (adv7511->i2c_main->addr << 1) + 4; + __adv7511_power_on(adv7511); + /* Reset the EDID_I2C_ADDR register as it might be cleared */ + regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, + edid_i2c_addr); + } + edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511); if (!adv7511->powered) -- cgit v1.2.1