diff options
author | Dave Airlie <airlied@redhat.com> | 2017-01-27 05:10:24 +0300 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2017-01-27 05:10:24 +0300 |
commit | 3875623c561f9e83181d9621e0a963ed457c4bf6 (patch) | |
tree | 21a2b0bb0bf4822bfe4bd7e16264d48c04b39e72 /drivers | |
parent | a7e2641aafe261bf70de01ff5fc68dea50468237 (diff) | |
parent | 6f897f51c4181397bf811d260eb7fef8d7ccd14f (diff) | |
download | linux-3875623c561f9e83181d9621e0a963ed457c4bf6.tar.xz |
Merge tag 'drm-misc-next-2017-01-23' of git://anongit.freedesktop.org/git/drm-misc into drm-next
- cleanups&fixes for dw-hdmi bride driver (Laurent)
- updates for adv bridge driver (John Stultz) for nexus
- drm_crtc_from_index helper rollout (Shawn Guo)
- removing drm_framebuffer_unregister_private from drivers&core
- target_vblank (Andrey Grodzovsky)
- misc tiny stuff
* tag 'drm-misc-next-2017-01-23' of git://anongit.freedesktop.org/git/drm-misc: (49 commits)
drm: qxl: Open code teardown function for qxl
drm: qxl: Open code probing sequence for qxl
drm/bridge: adv7511: Re-write the i2c address before EDID probing
drm/bridge: adv7511: Reuse __adv7511_power_on/off() when probing EDID
drm/bridge: adv7511: Rework adv7511_power_on/off() so they can be reused internally
drm/bridge: adv7511: Enable HPD interrupts to support hotplug and improve monitor detection
drm/bridge: adv7511: Switch to using drm_kms_helper_hotplug_event()
drm/bridge: adv7511: Use work_struct to defer hotplug handing to out of irq context
drm: vc4: use crtc helper drm_crtc_from_index()
drm: tegra: use crtc helper drm_crtc_from_index()
drm: nouveau: use crtc helper drm_crtc_from_index()
drm: mediatek: use crtc helper drm_crtc_from_index()
drm: kirin: use crtc helper drm_crtc_from_index()
drm: exynos: use crtc helper drm_crtc_from_index()
dt-bindings: display: dw-hdmi: Clean up DT bindings documentation
drm: bridge: dw-hdmi: Assert SVSRET before resetting the PHY
drm: bridge: dw-hdmi: Fix the name of the PHY reset macros
drm: bridge: dw-hdmi: Define and use macros for PHY register addresses
drm: bridge: dw-hdmi: Detect PHY type at runtime
drm: bridge: dw-hdmi: Handle overflow workaround based on device version
...
Diffstat (limited to 'drivers')
36 files changed, 778 insertions, 398 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 6f3f9e68c218..90bc65d07a35 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -138,7 +138,7 @@ config DRM_KMS_CMA_HELPER config DRM_VM bool - depends on DRM + depends on DRM && MMU source "drivers/gpu/drm/i2c/Kconfig" @@ -267,7 +267,7 @@ source "drivers/gpu/drm/meson/Kconfig" menuconfig DRM_LEGACY bool "Enable legacy drivers (DANGEROUS)" - depends on DRM + depends on DRM && MMU select DRM_VM help Enable legacy DRI1 drivers. Those drivers expose unsafe and dangerous diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig index 15f3ecfb16f1..eafaeeb7b5b1 100644 --- a/drivers/gpu/drm/armada/Kconfig +++ b/drivers/gpu/drm/armada/Kconfig @@ -1,6 +1,6 @@ config DRM_ARMADA tristate "DRM support for Marvell Armada SoCs" - depends on DRM && HAVE_CLK && ARM + depends on DRM && HAVE_CLK && ARM && MMU select DRM_KMS_HELPER help Support the "LCD" controllers found on the Marvell Armada 510 diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 992d76ce02bb..fe18a5d2d84b 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -12,6 +12,7 @@ #include <linux/hdmi.h> #include <linux/i2c.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_mipi_dsi.h> @@ -317,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; @@ -329,6 +332,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..f75ab6278113 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; @@ -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); } @@ -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; } @@ -402,6 +409,27 @@ 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); + enum drm_connector_status status; + unsigned int val; + int ret; + + 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) { unsigned int irq0, irq1; @@ -419,7 +447,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; @@ -546,23 +574,20 @@ static int adv7511_get_modes(struct adv7511 *adv7511, /* 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; + 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) - 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; @@ -825,6 +850,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; } @@ -839,6 +868,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 +1020,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 +1038,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 +1060,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 +1077,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 +1087,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); @@ -1006,6 +1099,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); @@ -1045,6 +1140,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 +1155,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); diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index f5009ae39b89..4fda0717e789 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -113,13 +113,20 @@ 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_encoder *encoder; - struct drm_bridge *bridge; + 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; @@ -133,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; @@ -868,7 +877,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 +891,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, @@ -903,11 +905,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) @@ -938,34 +940,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 prep, - 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; - if (prep) - return -EINVAL; - - 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,9 +986,13 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, /* 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); + /* 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); hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST); @@ -1015,21 +1001,26 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, 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); - - /* CURRCTRL */ - hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[res_idx], 0x10); + 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, 0x0000, 0x13); /* PLLPHBYCTRL */ - hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); + 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, 0x19); /* TXTERM */ - hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 0x09); /* CKSYMTXCTRL */ - hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 0x0E); /* VLEVCTRL */ + 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); - /* REMOVE CLK TERM */ - hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */ + /* 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); @@ -1041,10 +1032,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, dw_hdmi_phy_gen2_txpwron(hdmi, 1); dw_hdmi_phy_gen2_pddq(hdmi, 0); - if (hdmi->dev_type == RK3288_HDMI) - dw_hdmi_phy_enable_spare(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; @@ -1079,7 +1067,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, cscon); if (ret) return ret; } @@ -1351,19 +1339,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); } @@ -1586,42 +1593,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) { @@ -1714,7 +1685,63 @@ 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) +{ + 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 = { + .attach = dw_hdmi_bridge_attach, .enable = dw_hdmi_bridge_enable, .disable = dw_hdmi_bridge_disable, .mode_set = dw_hdmi_bridge_mode_set, @@ -1816,7 +1843,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); @@ -1826,67 +1854,80 @@ 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) -{ - struct drm_encoder *encoder = hdmi->encoder; - struct drm_bridge *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); - if (ret) { - DRM_ERROR("Failed to initialize bridge with drm\n"); - return -EINVAL; +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, } +}; - hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; +static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) +{ + unsigned int i; + u8 phy_type; - drm_connector_helper_add(&hdmi->connector, - &dw_hdmi_connector_helper_funcs); + phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID); - drm_connector_init(drm, &hdmi->connector, - &dw_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); + 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; + } + } - drm_mode_connector_attach_encoder(&hdmi->connector, encoder); + 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 0; + return -ENODEV; } -int dw_hdmi_bind(struct device *dev, struct device *master, - void *data, struct drm_encoder *encoder, - struct resource *iores, int irq, - 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 drm_device *drm = 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 prod_id0; + u8 prod_id1; u8 config0; - u8 config1; + u8 config3; hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) - return -ENOMEM; - - hdmi->connector.interlace_allowed = 1; + return ERR_PTR(-ENOMEM); hdmi->plat_data = plat_data; 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); @@ -1908,7 +1949,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master, 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); @@ -1917,13 +1958,14 @@ int dw_hdmi_bind(struct device *dev, struct device *master, 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 { 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); @@ -1957,15 +1999,36 @@ int dw_hdmi_bind(struct device *dev, struct device *master, } /* 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)); + 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", + hdmi->version, prod_id0, prod_id1); + ret = -ENODEV; + goto err_iahb; + } + + 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", + hdmi->phy->name); initialize_hdmi_ih_mutes(hdmi); + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto err_iahb; + } + ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq, dw_hdmi_irq, IRQF_SHARED, dev_name(dev), hdmi); @@ -1995,11 +2058,11 @@ int dw_hdmi_bind(struct device *dev, struct device *master, 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(drm, hdmi); + ret = dw_hdmi_fb_registered(hdmi); if (ret) goto err_iahb; @@ -2012,9 +2075,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master, 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; @@ -2046,9 +2109,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master, if (hdmi->i2c) dw_hdmi_i2c_init(hdmi); - dev_set_drvdata(dev, hdmi); + platform_set_drvdata(pdev, hdmi); - return 0; + return hdmi; err_iahb: if (hdmi->i2c) { @@ -2062,14 +2125,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, struct device *master, void *data) +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); @@ -2084,6 +2144,70 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) 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 <s.hauer@pengutronix.de>"); diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h index 55135bbd0c16..325b0b8ae639 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/dw-hdmi.h @@ -545,12 +545,24 @@ #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, /* 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, @@ -847,8 +859,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, @@ -977,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, @@ -1073,4 +1084,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__ */ diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 7b71ac48b8a4..b26e3419027e 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2708,6 +2708,44 @@ backoff: } EXPORT_SYMBOL(drm_atomic_helper_connector_set_property); +static int page_flip_common( + struct drm_atomic_state *state, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event) +{ + struct drm_plane *plane = crtc->primary; + struct drm_plane_state *plane_state; + struct drm_crtc_state *crtc_state; + int ret = 0; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + crtc_state->event = event; + + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); + + + ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); + if (ret != 0) + return ret; + drm_atomic_set_fb_for_plane(plane_state, fb); + + /* Make sure we don't accidentally do a full modeset. */ + state->allow_modeset = false; + if (!crtc_state->active) { + DRM_DEBUG_ATOMIC("[CRTC:%d] disabled, rejecting legacy flip\n", + crtc->base.id); + return -EINVAL; + } + + return ret; +} + /** * drm_atomic_helper_page_flip - execute a legacy page flip * @crtc: DRM crtc @@ -2715,7 +2753,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_set_property); * @event: optional DRM event to signal upon completion * @flags: flip flags for non-vblank sync'ed updates * - * Provides a default page flip implementation using the atomic driver interface. + * Provides a default &drm_crtc_funcs.page_flip implementation + * using the atomic driver interface. * * Note that for now so called async page flips (i.e. updates which are not * synchronized to vblank) are not supported, since the atomic interfaces have @@ -2723,6 +2762,9 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_set_property); * * Returns: * Returns 0 on success, negative errno numbers on failure. + * + * See also: + * drm_atomic_helper_page_flip_target() */ int drm_atomic_helper_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, @@ -2731,8 +2773,6 @@ int drm_atomic_helper_page_flip(struct drm_crtc *crtc, { struct drm_plane *plane = crtc->primary; struct drm_atomic_state *state; - struct drm_plane_state *plane_state; - struct drm_crtc_state *crtc_state; int ret = 0; if (flags & DRM_MODE_PAGE_FLIP_ASYNC) @@ -2743,35 +2783,86 @@ int drm_atomic_helper_page_flip(struct drm_crtc *crtc, return -ENOMEM; state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); + retry: - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); + ret = page_flip_common(state, crtc, fb, event); + if (ret != 0) goto fail; - } - crtc_state->event = event; - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - goto fail; - } + ret = drm_atomic_nonblocking_commit(state); - ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); +fail: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_put(state); + return ret; + +backoff: + drm_atomic_state_clear(state); + drm_atomic_legacy_backoff(state); + + /* + * Someone might have exchanged the framebuffer while we dropped locks + * in the backoff code. We need to fix up the fb refcount tracking the + * core does for us. + */ + plane->old_fb = plane->fb; + + goto retry; +} +EXPORT_SYMBOL(drm_atomic_helper_page_flip); + +/** + * drm_atomic_helper_page_flip_target - do page flip on target vblank period. + * @crtc: DRM crtc + * @fb: DRM framebuffer + * @event: optional DRM event to signal upon completion + * @flags: flip flags for non-vblank sync'ed updates + * @target: specifying the target vblank period when the flip to take effect + * + * Provides a default &drm_crtc_funcs.page_flip_target implementation. + * Similar to drm_atomic_helper_page_flip() with extra parameter to specify + * target vblank period to flip. + * + * Returns: + * Returns 0 on success, negative errno numbers on failure. + */ +int drm_atomic_helper_page_flip_target( + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t flags, + uint32_t target) +{ + struct drm_plane *plane = crtc->primary; + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + int ret = 0; + + if (flags & DRM_MODE_PAGE_FLIP_ASYNC) + return -EINVAL; + + state = drm_atomic_state_alloc(plane->dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); + +retry: + ret = page_flip_common(state, crtc, fb, event); if (ret != 0) goto fail; - drm_atomic_set_fb_for_plane(plane_state, fb); - /* Make sure we don't accidentally do a full modeset. */ - state->allow_modeset = false; - if (!crtc_state->active) { - DRM_DEBUG_ATOMIC("[CRTC:%d] disabled, rejecting legacy flip\n", - crtc->base.id); + crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + if (WARN_ON(!crtc_state)) { ret = -EINVAL; goto fail; } + crtc_state->target_vblank = target; ret = drm_atomic_nonblocking_commit(state); + fail: if (ret == -EDEADLK) goto backoff; @@ -2792,7 +2883,7 @@ backoff: goto retry; } -EXPORT_SYMBOL(drm_atomic_helper_page_flip); +EXPORT_SYMBOL(drm_atomic_helper_page_flip_target); /** * drm_atomic_helper_connector_dpms() - connector dpms helper implementation diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c index a7916e5f8864..5066638928ec 100644 --- a/drivers/gpu/drm/drm_cache.c +++ b/drivers/gpu/drm/drm_cache.c @@ -67,6 +67,14 @@ static void drm_cache_flush_clflush(struct page *pages[], } #endif +/** + * drm_clflush_pages - Flush dcache lines of a set of pages. + * @pages: List of pages to be flushed. + * @num_pages: Number of pages in the array. + * + * Flush every data cache line entry that points to an address belonging + * to a page in the array. + */ void drm_clflush_pages(struct page *pages[], unsigned long num_pages) { @@ -101,6 +109,13 @@ drm_clflush_pages(struct page *pages[], unsigned long num_pages) } EXPORT_SYMBOL(drm_clflush_pages); +/** + * drm_clflush_sg - Flush dcache lines pointing to a scather-gather. + * @st: struct sg_table. + * + * Flush every data cache line entry that points to an address in the + * sg. + */ void drm_clflush_sg(struct sg_table *st) { @@ -125,6 +140,14 @@ drm_clflush_sg(struct sg_table *st) } EXPORT_SYMBOL(drm_clflush_sg); +/** + * drm_clflush_virt_range - Flush dcache lines of a region + * @addr: Initial kernel memory address. + * @length: Region size. + * + * Flush every data cache line entry that points to an address in the + * region requested. + */ void drm_clflush_virt_range(void *addr, unsigned long length) { diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 20a4011f790d..4364abfb6a71 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -473,8 +473,7 @@ drm_fbdev_cma_create(struct drm_fb_helper *helper, return 0; err_cma_destroy: - drm_framebuffer_unregister_private(&fbdev_cma->fb->fb); - drm_fb_cma_destroy(&fbdev_cma->fb->fb); + drm_framebuffer_remove(&fbdev_cma->fb->fb); err_fb_info_destroy: drm_fb_helper_release_fbi(helper); err_gem_free_object: @@ -574,10 +573,8 @@ void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma) drm_fbdev_cma_defio_fini(fbdev_cma->fb_helper.fbdev); drm_fb_helper_release_fbi(&fbdev_cma->fb_helper); - if (fbdev_cma->fb) { - drm_framebuffer_unregister_private(&fbdev_cma->fb->fb); - drm_fb_cma_destroy(&fbdev_cma->fb->fb); - } + if (fbdev_cma->fb) + drm_framebuffer_remove(&fbdev_cma->fb->fb); drm_fb_helper_fini(&fbdev_cma->fb_helper); kfree(fbdev_cma); diff --git a/drivers/gpu/drm/etnaviv/Kconfig b/drivers/gpu/drm/etnaviv/Kconfig index 2cde7a5442fb..656c061b439d 100644 --- a/drivers/gpu/drm/etnaviv/Kconfig +++ b/drivers/gpu/drm/etnaviv/Kconfig @@ -3,6 +3,7 @@ config DRM_ETNAVIV tristate "ETNAVIV (DRM support for Vivante GPU IP cores)" depends on DRM depends on ARCH_MXC || ARCH_DOVE + depends on MMU select SHMEM select TMPFS select IOMMU_API diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c index c6745a1b8e6a..d69af00bdd6a 100644 --- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c @@ -467,7 +467,7 @@ err: clk_disable_unprepare(ctx->clks[i]); } -static struct exynos_drm_crtc_ops decon_crtc_ops = { +static const struct exynos_drm_crtc_ops decon_crtc_ops = { .enable = decon_enable, .disable = decon_disable, .enable_vblank = decon_enable_vblank, diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 2530bf57716a..309c8ee52524 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -109,9 +109,6 @@ static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct exynos_drm_private *private = crtc->dev->dev_private; - - private->crtc[exynos_crtc->pipe] = NULL; drm_crtc_cleanup(crtc); kfree(exynos_crtc); @@ -134,7 +131,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, void *ctx) { struct exynos_drm_crtc *exynos_crtc; - struct exynos_drm_private *private = drm_dev->dev_private; struct drm_crtc *crtc; int ret; @@ -149,8 +145,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, crtc = &exynos_crtc->base; - private->crtc[pipe] = crtc; - ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL, &exynos_crtc_funcs, NULL); if (ret < 0) diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 80c4d5b81689..cf6e08cb35a7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -211,12 +211,6 @@ struct drm_exynos_file_private { struct exynos_drm_private { struct drm_fb_helper *fb_helper; - /* - * created crtc object would be contained at this array and - * this array is used to be aware of which crtc did it request vblank. - */ - struct drm_crtc *crtc[MAX_CRTC]; - struct device *dma_dev; void *mapping; @@ -231,9 +225,9 @@ struct exynos_drm_private { static inline struct exynos_drm_crtc * exynos_drm_crtc_from_pipe(struct drm_device *dev, int pipe) { - struct exynos_drm_private *private = dev->dev_private; + struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); - return to_exynos_crtc(private->crtc[pipe]); + return to_exynos_crtc(crtc); } static inline struct device *to_dma_dev(struct drm_device *dev) diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index 307d460ab684..9a0678a33e0d 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -304,8 +304,8 @@ static void ade_set_medianoc_qos(struct ade_crtc *acrtc) static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe) { - struct kirin_drm_private *priv = dev->dev_private; - struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]); + struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); + struct ade_crtc *acrtc = to_ade_crtc(crtc); struct ade_hw_ctx *ctx = acrtc->ctx; void __iomem *base = ctx->base; @@ -320,8 +320,8 @@ static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe) static void ade_disable_vblank(struct drm_device *dev, unsigned int pipe) { - struct kirin_drm_private *priv = dev->dev_private; - struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]); + struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); + struct ade_crtc *acrtc = to_ade_crtc(crtc); struct ade_hw_ctx *ctx = acrtc->ctx; void __iomem *base = ctx->base; @@ -575,7 +575,6 @@ static const struct drm_crtc_funcs ade_crtc_funcs = { static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, struct drm_plane *plane) { - struct kirin_drm_private *priv = dev->dev_private; struct device_node *port; int ret; @@ -599,7 +598,6 @@ static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, } drm_crtc_helper_add(crtc, &ade_crtc_helper_funcs); - priv->crtc[drm_crtc_index(crtc)] = crtc; return 0; } diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h index a0bb217c4c64..7f60c64915d9 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h @@ -20,7 +20,6 @@ struct kirin_dc_ops { }; struct kirin_drm_private { - struct drm_crtc *crtc[MAX_CRTC]; #ifdef CONFIG_DRM_FBDEV_EMULATION struct drm_fbdev_cma *fbdev; #endif diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ead3b6417c54..244628065f94 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -49,6 +49,7 @@ #include <drm/drm_legacy.h> /* for struct drm_dma_handle */ #include <drm/drm_gem.h> #include <drm/drm_auth.h> +#include <drm/drm_cache.h> #include "i915_params.h" #include "i915_reg.h" diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c index 359cd2765552..f645275e6e63 100644 --- a/drivers/gpu/drm/imx/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -207,8 +207,6 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master, struct drm_device *drm = data; struct drm_encoder *encoder; struct imx_hdmi *hdmi; - struct resource *iores; - int irq; int ret; if (!pdev->dev.of_node) @@ -223,14 +221,6 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master, hdmi->dev = &pdev->dev; encoder = &hdmi->encoder; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) - return -ENXIO; - encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); /* * If we failed to find the CRTC(s) which this encoder is @@ -249,7 +239,7 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master, drm_encoder_init(drm, encoder, &dw_hdmi_imx_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); - ret = dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data); + ret = dw_hdmi_bind(pdev, encoder, plat_data); /* * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), @@ -264,7 +254,7 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master, static void dw_hdmi_imx_unbind(struct device *dev, struct device *master, void *data) { - return dw_hdmi_unbind(dev, master, data); + return dw_hdmi_unbind(dev); } static const struct component_ops dw_hdmi_imx_ops = { diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c index 01a21dd835b5..a73de1e669c2 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c @@ -170,8 +170,8 @@ static void mtk_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe) { - struct mtk_drm_private *priv = drm->dev_private; - struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]); + struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe); + struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0]; mtk_ddp_comp_enable_vblank(ovl, &mtk_crtc->base); @@ -181,8 +181,8 @@ int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe) void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe) { - struct mtk_drm_private *priv = drm->dev_private; - struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]); + struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe); + struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0]; mtk_ddp_comp_disable_vblank(ovl); @@ -588,7 +588,6 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev, goto unprepare; drm_mode_crtc_set_gamma_size(&mtk_crtc->base, MTK_LUT_SIZE); drm_crtc_enable_color_mgmt(&mtk_crtc->base, 0, false, MTK_LUT_SIZE); - priv->crtc[pipe] = &mtk_crtc->base; priv->num_pipes++; return 0; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h index aa9389446785..df322a7a5fcb 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h @@ -32,7 +32,6 @@ struct mtk_drm_private { struct drm_device *drm; struct device *dma_dev; - struct drm_crtc *crtc[MAX_CRTC]; unsigned int num_pipes; struct device_node *mutex_node; diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index d96b2b6898a3..7f78da695dff 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -4,6 +4,7 @@ config DRM_MSM depends on DRM depends on ARCH_QCOM || (ARM && COMPILE_TEST) depends on OF && COMMON_CLK + depends on MMU select REGULATOR select DRM_KMS_HELPER select DRM_PANEL diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 5d68ab362d75..f8a587eac6b8 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -174,10 +174,8 @@ fail_unlock: fail: if (ret) { - if (fb) { - drm_framebuffer_unregister_private(fb); + if (fb) drm_framebuffer_remove(fb); - } } return ret; @@ -247,7 +245,6 @@ void msm_fbdev_free(struct drm_device *dev) /* this will free the backing object */ if (fbdev->fb) { msm_gem_put_vaddr(fbdev->bo); - drm_framebuffer_unregister_private(fbdev->fb); drm_framebuffer_remove(fbdev->fb); } diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index add353e230f4..6b570079d185 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -58,27 +58,30 @@ int nouveau_display_vblank_enable(struct drm_device *dev, unsigned int pipe) { struct drm_crtc *crtc; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - if (nv_crtc->index == pipe) { - nvif_notify_get(&nv_crtc->vblank); - return 0; - } - } - return -EINVAL; + struct nouveau_crtc *nv_crtc; + + crtc = drm_crtc_from_index(dev, pipe); + if (!crtc) + return -EINVAL; + + nv_crtc = nouveau_crtc(crtc); + nvif_notify_get(&nv_crtc->vblank); + + return 0; } void nouveau_display_vblank_disable(struct drm_device *dev, unsigned int pipe) { struct drm_crtc *crtc; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - if (nv_crtc->index == pipe) { - nvif_notify_put(&nv_crtc->vblank); - return; - } - } + struct nouveau_crtc *nv_crtc; + + crtc = drm_crtc_from_index(dev, pipe); + if (!crtc) + return; + + nv_crtc = nouveau_crtc(crtc); + nvif_notify_put(&nv_crtc->vblank); } static inline int diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index aed99a0fc44b..2a839956dae6 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -225,10 +225,8 @@ fail: drm_fb_helper_release_fbi(helper); - if (fb) { - drm_framebuffer_unregister_private(fb); + if (fb) drm_framebuffer_remove(fb); - } } return ret; @@ -314,10 +312,8 @@ void omap_fbdev_free(struct drm_device *dev) omap_gem_put_paddr(fbdev->bo); /* this will free the backing object */ - if (fbdev->fb) { - drm_framebuffer_unregister_private(fbdev->fb); + if (fbdev->fb) drm_framebuffer_remove(fbdev->fb); - } kfree(fbdev); diff --git a/drivers/gpu/drm/qxl/qxl_debugfs.c b/drivers/gpu/drm/qxl/qxl_debugfs.c index 241af9131dc8..057b2b547cac 100644 --- a/drivers/gpu/drm/qxl/qxl_debugfs.c +++ b/drivers/gpu/drm/qxl/qxl_debugfs.c @@ -84,8 +84,18 @@ int qxl_debugfs_init(struct drm_minor *minor) { #if defined(CONFIG_DEBUG_FS) + int r; + struct qxl_device *dev = + (struct qxl_device *) minor->dev->dev_private; + drm_debugfs_create_files(qxl_debugfs_list, QXL_DEBUGFS_ENTRIES, minor->debugfs_root, minor); + + r = qxl_ttm_debugfs_init(dev); + if (r) { + DRM_ERROR("Failed to init TTM debugfs\n"); + return r; + } #endif return 0; } diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 659c77742649..416ade8566b7 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -1077,7 +1077,6 @@ static int qdev_output_init(struct drm_device *dev, int num_output) dev->mode_config.suggested_x_property, 0); drm_object_attach_property(&connector->base, dev->mode_config.suggested_y_property, 0); - drm_connector_register(connector); return 0; } diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index 460bbceae297..6e0f8a2d8ac9 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -62,20 +62,83 @@ static struct pci_driver qxl_pci_driver; static int qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + struct drm_device *drm; + struct qxl_device *qdev; + int ret; + if (pdev->revision < 4) { DRM_ERROR("qxl too old, doesn't support client_monitors_config," " use xf86-video-qxl in user mode"); return -EINVAL; /* TODO: ENODEV ? */ } - return drm_get_pci_dev(pdev, ent, &qxl_driver); + + drm = drm_dev_alloc(&qxl_driver, &pdev->dev); + if (IS_ERR(drm)) + return -ENOMEM; + + qdev = kzalloc(sizeof(struct qxl_device), GFP_KERNEL); + if (!qdev) { + ret = -ENOMEM; + goto free_drm_device; + } + + ret = pci_enable_device(pdev); + if (ret) + goto free_drm_device; + + drm->pdev = pdev; + pci_set_drvdata(pdev, drm); + drm->dev_private = qdev; + + ret = qxl_device_init(qdev, drm, pdev, ent->driver_data); + if (ret) + goto disable_pci; + + ret = drm_vblank_init(drm, 1); + if (ret) + goto unload; + + ret = qxl_modeset_init(qdev); + if (ret) + goto vblank_cleanup; + + drm_kms_helper_poll_init(qdev->ddev); + + /* Complete initialization. */ + ret = drm_dev_register(drm, ent->driver_data); + if (ret) + goto modeset_cleanup; + + return 0; + +modeset_cleanup: + qxl_modeset_fini(qdev); +vblank_cleanup: + drm_vblank_cleanup(drm); +unload: + qxl_device_fini(qdev); +disable_pci: + pci_disable_device(pdev); +free_drm_device: + kfree(qdev); + kfree(drm); + return ret; } static void qxl_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); + struct qxl_device *qdev = dev->dev_private; + + drm_dev_unregister(dev); + + qxl_modeset_fini(qdev); + qxl_device_fini(qdev); - drm_put_dev(dev); + dev->dev_private = NULL; + kfree(qdev); + drm_dev_unref(dev); } static const struct file_operations qxl_fops = { @@ -230,8 +293,6 @@ static struct pci_driver qxl_pci_driver = { static struct drm_driver qxl_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, - .load = qxl_driver_load, - .unload = qxl_driver_unload, .get_vblank_counter = qxl_noop_get_vblank_counter, .enable_vblank = qxl_noop_enable_vblank, .disable_vblank = qxl_noop_disable_vblank, diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 883d8639c04e..0d877fa61162 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -336,8 +336,9 @@ __printf(2,3) void qxl_io_log(struct qxl_device *qdev, const char *fmt, ...); extern const struct drm_ioctl_desc qxl_ioctls[]; extern int qxl_max_ioctl; -int qxl_driver_load(struct drm_device *dev, unsigned long flags); -void qxl_driver_unload(struct drm_device *dev); +int qxl_device_init(struct qxl_device *qdev, struct drm_device *ddev, + struct pci_dev *pdev, unsigned long flags); +void qxl_device_fini(struct qxl_device *qdev); int qxl_modeset_init(struct qxl_device *qdev); void qxl_modeset_fini(struct qxl_device *qdev); @@ -531,6 +532,7 @@ int qxl_garbage_collect(struct qxl_device *qdev); int qxl_debugfs_init(struct drm_minor *minor); void qxl_debugfs_takedown(struct drm_minor *minor); +int qxl_ttm_debugfs_init(struct qxl_device *qdev); /* qxl_prime.c */ int qxl_gem_prime_pin(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index b2491407b616..d0666f5dccd6 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -115,7 +115,7 @@ static void qxl_gc_work(struct work_struct *work) qxl_garbage_collect(qdev); } -static int qxl_device_init(struct qxl_device *qdev, +int qxl_device_init(struct qxl_device *qdev, struct drm_device *ddev, struct pci_dev *pdev, unsigned long flags) @@ -263,7 +263,7 @@ static int qxl_device_init(struct qxl_device *qdev, return 0; } -static void qxl_device_fini(struct qxl_device *qdev) +void qxl_device_fini(struct qxl_device *qdev) { if (qdev->current_release_bo[0]) qxl_bo_unref(&qdev->current_release_bo[0]); @@ -284,55 +284,3 @@ static void qxl_device_fini(struct qxl_device *qdev) qdev->mode_info.num_modes = 0; qxl_debugfs_remove_files(qdev); } - -void qxl_driver_unload(struct drm_device *dev) -{ - struct qxl_device *qdev = dev->dev_private; - - if (qdev == NULL) - return; - - drm_vblank_cleanup(dev); - - qxl_modeset_fini(qdev); - qxl_device_fini(qdev); - - kfree(qdev); - dev->dev_private = NULL; -} - -int qxl_driver_load(struct drm_device *dev, unsigned long flags) -{ - struct qxl_device *qdev; - int r; - - qdev = kzalloc(sizeof(struct qxl_device), GFP_KERNEL); - if (qdev == NULL) - return -ENOMEM; - - dev->dev_private = qdev; - - r = qxl_device_init(qdev, dev, dev->pdev, flags); - if (r) - goto out; - - r = drm_vblank_init(dev, 1); - if (r) - goto unload; - - r = qxl_modeset_init(qdev); - if (r) - goto unload; - - drm_kms_helper_poll_init(qdev->ddev); - - return 0; -unload: - qxl_driver_unload(dev); - -out: - kfree(qdev); - return r; -} - - diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index 1b096c5252ad..bc1c896bc5e1 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -35,7 +35,6 @@ #include "qxl_object.h" #include <linux/delay.h> -static int qxl_ttm_debugfs_init(struct qxl_device *qdev); static struct qxl_device *qxl_get_qdev(struct ttm_bo_device *bdev) { @@ -436,11 +435,6 @@ int qxl_ttm_init(struct qxl_device *qdev) ((unsigned)num_io_pages * PAGE_SIZE) / (1024 * 1024)); DRM_INFO("qxl: %uM of Surface memory size\n", (unsigned)qdev->surfaceram_size / (1024 * 1024)); - r = qxl_ttm_debugfs_init(qdev); - if (r) { - DRM_ERROR("Failed to init debugfs\n"); - return r; - } return 0; } @@ -473,7 +467,7 @@ static int qxl_mm_dump_table(struct seq_file *m, void *data) } #endif -static int qxl_ttm_debugfs_init(struct qxl_device *qdev) +int qxl_ttm_debugfs_init(struct qxl_device *qdev) { #if defined(CONFIG_DEBUG_FS) static struct drm_info_list qxl_mem_types_list[QXL_DEBUGFS_MEM_TYPES]; diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 0665fb915579..a6d4a0236e8f 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -257,8 +257,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, struct drm_device *drm = data; struct drm_encoder *encoder; struct rockchip_hdmi *hdmi; - struct resource *iores; - int irq; int ret; if (!pdev->dev.of_node) @@ -273,14 +271,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, hdmi->dev = &pdev->dev; encoder = &hdmi->encoder; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) - return -ENXIO; - encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); /* * If we failed to find the CRTC(s) which this encoder is @@ -301,7 +291,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); - ret = dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data); + ret = dw_hdmi_bind(pdev, encoder, plat_data); /* * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), @@ -316,7 +306,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, void *data) { - return dw_hdmi_unbind(dev, master, data); + return dw_hdmi_unbind(dev); } static const struct component_ops dw_hdmi_rockchip_ops = { diff --git a/drivers/gpu/drm/selftests/test-drm_mm.c b/drivers/gpu/drm/selftests/test-drm_mm.c index 6d2a5cd211f3..6df53e6c1308 100644 --- a/drivers/gpu/drm/selftests/test-drm_mm.c +++ b/drivers/gpu/drm/selftests/test-drm_mm.c @@ -1274,13 +1274,19 @@ static bool evict_everything(struct drm_mm *mm, if (drm_mm_scan_add_block(&scan, &e->node)) break; } + + err = 0; list_for_each_entry(e, &evict_list, link) { if (!drm_mm_scan_remove_block(&scan, &e->node)) { - pr_err("Node %lld not marked for eviction!\n", - e->node.start); - list_del(&e->link); + if (!err) { + pr_err("Node %lld not marked for eviction!\n", + e->node.start); + err = -EINVAL; + } } } + if (err) + return false; list_for_each_entry(e, &evict_list, link) drm_mm_remove_node(&e->node); diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 0f4eacb0af4f..2d57f6278db1 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -804,23 +804,10 @@ static const struct file_operations tegra_drm_fops = { .llseek = noop_llseek, }; -static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, - unsigned int pipe) -{ - struct drm_crtc *crtc; - - list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) { - if (pipe == drm_crtc_index(crtc)) - return crtc; - } - - return NULL; -} - static u32 tegra_drm_get_vblank_counter(struct drm_device *drm, unsigned int pipe) { - struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); + struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe); struct tegra_dc *dc = to_tegra_dc(crtc); if (!crtc) @@ -831,7 +818,7 @@ static u32 tegra_drm_get_vblank_counter(struct drm_device *drm, static int tegra_drm_enable_vblank(struct drm_device *drm, unsigned int pipe) { - struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); + struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe); struct tegra_dc *dc = to_tegra_dc(crtc); if (!crtc) @@ -844,7 +831,7 @@ static int tegra_drm_enable_vblank(struct drm_device *drm, unsigned int pipe) static void tegra_drm_disable_vblank(struct drm_device *drm, unsigned int pipe) { - struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); + struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe); struct tegra_dc *dc = to_tegra_dc(crtc); if (crtc) diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 8df7783cecc2..f896e2ff7d47 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -271,8 +271,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, return 0; destroy: - drm_framebuffer_unregister_private(fb); - tegra_fb_destroy(fb); + drm_framebuffer_remove(fb); release: drm_fb_helper_release_fbi(helper); return err; @@ -342,10 +341,8 @@ static void tegra_fbdev_exit(struct tegra_fbdev *fbdev) drm_fb_helper_unregister_fbi(&fbdev->base); drm_fb_helper_release_fbi(&fbdev->base); - if (fbdev->fb) { - drm_framebuffer_unregister_private(&fbdev->fb->base); + if (fbdev->fb) drm_framebuffer_remove(&fbdev->fb->base); - } drm_fb_helper_fini(&fbdev->base); tegra_fbdev_free(fbdev); diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index a0fd3e66bc4b..75b708b36890 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -156,7 +156,8 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, const struct drm_display_mode *mode) { struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; + struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); u32 val; int fifo_lines; int vblank_lines; @@ -272,9 +273,7 @@ int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id, int *max_error, struct timeval *vblank_time, unsigned flags) { - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; - struct drm_crtc *crtc = &vc4_crtc->base; + struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id); struct drm_crtc_state *state = crtc->state; /* Helper routine in DRM core does all the work: */ @@ -652,8 +651,8 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc, int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id) { - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; + struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); CRTC_WRITE(PV_INTEN, PV_INT_VFP_START); @@ -662,8 +661,8 @@ int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id) void vc4_disable_vblank(struct drm_device *dev, unsigned int crtc_id) { - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; + struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); CRTC_WRITE(PV_INTEN, 0); } @@ -937,7 +936,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_crtc *vc4_crtc; struct drm_crtc *crtc; struct drm_plane *primary_plane, *cursor_plane, *destroy_plane, *temp; @@ -975,7 +973,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) &vc4_crtc_funcs, NULL); drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs); primary_plane->crtc = crtc; - vc4->crtc[drm_crtc_index(crtc)] = vc4_crtc; vc4_crtc->channel = vc4_crtc->data->hvs_channel; drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 489956efbff8..affcdeb70aa9 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -16,7 +16,6 @@ struct vc4_dev { struct vc4_hdmi *hdmi; struct vc4_hvs *hvs; - struct vc4_crtc *crtc[3]; struct vc4_v3d *v3d; struct vc4_dpi *dpi; struct vc4_vec *vec; diff --git a/drivers/gpu/drm/vgem/vgem_drv.h b/drivers/gpu/drm/vgem/vgem_drv.h index 1f8798ad329c..cb59c7ab98b9 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.h +++ b/drivers/gpu/drm/vgem/vgem_drv.h @@ -31,6 +31,7 @@ #include <drm/drmP.h> #include <drm/drm_gem.h> +#include <drm/drm_cache.h> #include <uapi/drm/vgem_drm.h> |