diff options
author | Dave Airlie <airlied@redhat.com> | 2020-09-23 02:49:48 +0300 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2020-09-23 02:52:24 +0300 |
commit | 6ea6be77086f23d4b346c9946dae24593befda2e (patch) | |
tree | dc3926a543ed2b8270aa111d97ee603970560eda /drivers/gpu/drm/bridge | |
parent | fc88fef916e8971eefeacc62241b7408b7e7939d (diff) | |
parent | 089d83418914abd4d908db117d9a3eca7f51a68c (diff) | |
download | linux-6ea6be77086f23d4b346c9946dae24593befda2e.tar.xz |
Merge tag 'drm-misc-next-2020-09-21' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 5.10:
UAPI Changes:
Cross-subsystem Changes:
- virtio: Merged a PR for patches that will affect drm/virtio
Core Changes:
- dev: More devm_drm convertions and removal of drm_dev_init
- atomic: Split out drm_atomic_helper_calc_timestamping_constants of
drm_atomic_helper_update_legacy_modeset_state
- ttm: More rework
Driver Changes:
- i915: selftests improvements
- panfrost: support for Amlogic SoC
- vc4: one fix
- tree-wide: conversions to devm_drm_dev_alloc,
- ast: simplifications of the atomic modesetting code
- panfrost: multiple fixes
- vc4: multiple fixes
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maxime Ripard <maxime@cerno.tech>
Link: https://patchwork.freedesktop.org/patch/msgid/20200921152956.2gxnsdgxmwhvjyut@gilmour.lan
Diffstat (limited to 'drivers/gpu/drm/bridge')
-rw-r--r-- | drivers/gpu/drm/bridge/parade-ps8640.c | 68 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 155 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/tc358775.c | 2 |
3 files changed, 191 insertions, 34 deletions
diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index 9f7b7a9c53c5..7bd0affa057a 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -65,6 +65,7 @@ struct ps8640 { struct regulator_bulk_data supplies[2]; struct gpio_desc *gpio_reset; struct gpio_desc *gpio_powerdown; + bool powered; }; static inline struct ps8640 *bridge_to_ps8640(struct drm_bridge *e) @@ -91,13 +92,15 @@ static int ps8640_bridge_vdo_control(struct ps8640 *ps_bridge, return 0; } -static void ps8640_pre_enable(struct drm_bridge *bridge) +static void ps8640_bridge_poweron(struct ps8640 *ps_bridge) { - struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); struct i2c_client *client = ps_bridge->page[PAGE2_TOP_CNTL]; unsigned long timeout; int ret, status; + if (ps_bridge->powered) + return; + ret = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies), ps_bridge->supplies); if (ret < 0) { @@ -152,10 +155,6 @@ static void ps8640_pre_enable(struct drm_bridge *bridge) goto err_regulators_disable; } - ret = ps8640_bridge_vdo_control(ps_bridge, ENABLE); - if (ret) - goto err_regulators_disable; - /* Switch access edp panel's edid through i2c */ ret = i2c_smbus_write_byte_data(client, PAGE2_I2C_BYPASS, I2C_BYPASS_EN); @@ -164,6 +163,8 @@ static void ps8640_pre_enable(struct drm_bridge *bridge) goto err_regulators_disable; } + ps_bridge->powered = true; + return; err_regulators_disable: @@ -171,12 +172,12 @@ err_regulators_disable: ps_bridge->supplies); } -static void ps8640_post_disable(struct drm_bridge *bridge) +static void ps8640_bridge_poweroff(struct ps8640 *ps_bridge) { - struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); int ret; - ps8640_bridge_vdo_control(ps_bridge, DISABLE); + if (!ps_bridge->powered) + return; gpiod_set_value(ps_bridge->gpio_reset, 1); gpiod_set_value(ps_bridge->gpio_powerdown, 1); @@ -184,6 +185,28 @@ static void ps8640_post_disable(struct drm_bridge *bridge) ps_bridge->supplies); if (ret < 0) DRM_ERROR("cannot disable regulators %d\n", ret); + + ps_bridge->powered = false; +} + +static void ps8640_pre_enable(struct drm_bridge *bridge) +{ + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); + int ret; + + ps8640_bridge_poweron(ps_bridge); + + ret = ps8640_bridge_vdo_control(ps_bridge, ENABLE); + if (ret < 0) + ps8640_bridge_poweroff(ps_bridge); +} + +static void ps8640_post_disable(struct drm_bridge *bridge) +{ + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); + + ps8640_bridge_vdo_control(ps_bridge, DISABLE); + ps8640_bridge_poweroff(ps_bridge); } static int ps8640_bridge_attach(struct drm_bridge *bridge, @@ -249,9 +272,34 @@ static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge, struct drm_connector *connector) { struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); + bool poweroff = !ps_bridge->powered; + struct edid *edid; + + /* + * When we end calling get_edid() triggered by an ioctl, i.e + * + * drm_mode_getconnector (ioctl) + * -> drm_helper_probe_single_connector_modes + * -> drm_bridge_connector_get_modes + * -> ps8640_bridge_get_edid + * + * We need to make sure that what we need is enabled before reading + * EDID, for this chip, we need to do a full poweron, otherwise it will + * fail. + */ + drm_bridge_chain_pre_enable(bridge); - return drm_get_edid(connector, + edid = drm_get_edid(connector, ps_bridge->page[PAGE0_DP_CNTL]->adapter); + + /* + * If we call the get_edid() function without having enabled the chip + * before, return the chip to its original power state. + */ + if (poweroff) + drm_bridge_chain_post_disable(bridge); + + return edid; } static const struct drm_bridge_funcs ps8640_bridge_funcs = { diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index d580b2aa4ce9..6b268f9445b3 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -89,7 +89,9 @@ #define VID_MODE_TYPE_NON_BURST_SYNC_EVENTS 0x1 #define VID_MODE_TYPE_BURST 0x2 #define VID_MODE_TYPE_MASK 0x3 +#define ENABLE_LOW_POWER_CMD BIT(15) #define VID_MODE_VPG_ENABLE BIT(16) +#define VID_MODE_VPG_MODE BIT(20) #define VID_MODE_VPG_HORIZONTAL BIT(24) #define DSI_VID_PKT_SIZE 0x3c @@ -220,6 +222,21 @@ #define PHY_STATUS_TIMEOUT_US 10000 #define CMD_PKT_STATUS_TIMEOUT_US 20000 +#ifdef CONFIG_DEBUG_FS +#define VPG_DEFS(name, dsi) \ + ((void __force *)&((*dsi).vpg_defs.name)) + +#define REGISTER(name, mask, dsi) \ + { #name, VPG_DEFS(name, dsi), mask, dsi } + +struct debugfs_entries { + const char *name; + bool *reg; + u32 mask; + struct dw_mipi_dsi *dsi; +}; +#endif /* CONFIG_DEBUG_FS */ + struct dw_mipi_dsi { struct drm_bridge bridge; struct mipi_dsi_host dsi_host; @@ -237,9 +254,12 @@ struct dw_mipi_dsi { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; - - bool vpg; - bool vpg_horizontal; + struct debugfs_entries *debugfs_vpg; + struct { + bool vpg; + bool vpg_horizontal; + bool vpg_ber_pattern; + } vpg_defs; #endif /* CONFIG_DEBUG_FS */ struct dw_mipi_dsi *master; /* dual-dsi master ptr */ @@ -360,13 +380,28 @@ static void dw_mipi_message_config(struct dw_mipi_dsi *dsi, bool lpm = msg->flags & MIPI_DSI_MSG_USE_LPM; u32 val = 0; + /* + * TODO dw drv improvements + * largest packet sizes during hfp or during vsa/vpb/vfp + * should be computed according to byte lane, lane number and only + * if sending lp cmds in high speed is enable (PHY_TXREQUESTCLKHS) + */ + dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(16) + | INVACT_LPCMD_TIME(4)); + if (msg->flags & MIPI_DSI_MSG_REQ_ACK) val |= ACK_RQST_EN; if (lpm) val |= CMD_MODE_ALL_LP; - dsi_write(dsi, DSI_LPCLK_CTRL, lpm ? 0 : PHY_TXREQUESTCLKHS); dsi_write(dsi, DSI_CMD_MODE_CFG, val); + + val = dsi_read(dsi, DSI_VID_MODE_CFG); + if (lpm) + val |= ENABLE_LOW_POWER_CMD; + else + val &= ~ENABLE_LOW_POWER_CMD; + dsi_write(dsi, DSI_VID_MODE_CFG, val); } static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val) @@ -529,9 +564,11 @@ static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi) val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS; #ifdef CONFIG_DEBUG_FS - if (dsi->vpg) { + if (dsi->vpg_defs.vpg) { val |= VID_MODE_VPG_ENABLE; - val |= dsi->vpg_horizontal ? VID_MODE_VPG_HORIZONTAL : 0; + val |= dsi->vpg_defs.vpg_horizontal ? + VID_MODE_VPG_HORIZONTAL : 0; + val |= dsi->vpg_defs.vpg_ber_pattern ? VID_MODE_VPG_MODE : 0; } #endif /* CONFIG_DEBUG_FS */ @@ -541,16 +578,22 @@ static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi) static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi, unsigned long mode_flags) { + u32 val; + dsi_write(dsi, DSI_PWR_UP, RESET); if (mode_flags & MIPI_DSI_MODE_VIDEO) { dsi_write(dsi, DSI_MODE_CFG, ENABLE_VIDEO_MODE); dw_mipi_dsi_video_mode_config(dsi); - dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS); } else { dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE); } + val = PHY_TXREQUESTCLKHS; + if (dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) + val |= AUTO_CLKLANE_CTRL; + dsi_write(dsi, DSI_LPCLK_CTRL, val); + dsi_write(dsi, DSI_PWR_UP, POWERUP); } @@ -562,15 +605,30 @@ static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi) static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi) { + const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops; + unsigned int esc_rate; /* in MHz */ + u32 esc_clk_division; + int ret; + /* * The maximum permitted escape clock is 20MHz and it is derived from - * lanebyteclk, which is running at "lane_mbps / 8". Thus we want: - * - * (lane_mbps >> 3) / esc_clk_division < 20 + * lanebyteclk, which is running at "lane_mbps / 8". + */ + if (phy_ops->get_esc_clk_rate) { + ret = phy_ops->get_esc_clk_rate(dsi->plat_data->priv_data, + &esc_rate); + if (ret) + DRM_DEBUG_DRIVER("Phy get_esc_clk_rate() failed\n"); + } else + esc_rate = 20; /* Default to 20MHz */ + + /* + * We want : + * (lane_mbps >> 3) / esc_clk_division < X * which is: - * (lane_mbps >> 3) / 20 > esc_clk_division + * (lane_mbps >> 3) / X > esc_clk_division */ - u32 esc_clk_division = (dsi->lane_mbps >> 3) / 20 + 1; + esc_clk_division = (dsi->lane_mbps >> 3) / esc_rate + 1; dsi_write(dsi, DSI_PWR_UP, RESET); @@ -611,14 +669,6 @@ static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi, dsi_write(dsi, DSI_DPI_VCID, DPI_VCID(dsi->channel)); dsi_write(dsi, DSI_DPI_COLOR_CODING, color); dsi_write(dsi, DSI_DPI_CFG_POL, val); - /* - * TODO dw drv improvements - * largest packet sizes during hfp or during vsa/vpb/vfp - * should be computed according to byte lane, lane number and only - * if sending lp cmds in high speed is enable (PHY_TXREQUESTCLKHS) - */ - dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4) - | INVACT_LPCMD_TIME(4)); } static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi) @@ -964,6 +1014,66 @@ static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = { #ifdef CONFIG_DEBUG_FS +static int dw_mipi_dsi_debugfs_write(void *data, u64 val) +{ + struct debugfs_entries *vpg = data; + struct dw_mipi_dsi *dsi; + u32 mode_cfg; + + if (!vpg) + return -ENODEV; + + dsi = vpg->dsi; + + *vpg->reg = (bool)val; + + mode_cfg = dsi_read(dsi, DSI_VID_MODE_CFG); + + if (*vpg->reg) + mode_cfg |= vpg->mask; + else + mode_cfg &= ~vpg->mask; + + dsi_write(dsi, DSI_VID_MODE_CFG, mode_cfg); + + return 0; +} + +static int dw_mipi_dsi_debugfs_show(void *data, u64 *val) +{ + struct debugfs_entries *vpg = data; + + if (!vpg) + return -ENODEV; + + *val = *vpg->reg; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, dw_mipi_dsi_debugfs_show, + dw_mipi_dsi_debugfs_write, "%llu\n"); + +static void debugfs_create_files(void *data) +{ + struct dw_mipi_dsi *dsi = data; + struct debugfs_entries debugfs[] = { + REGISTER(vpg, VID_MODE_VPG_ENABLE, dsi), + REGISTER(vpg_horizontal, VID_MODE_VPG_HORIZONTAL, dsi), + REGISTER(vpg_ber_pattern, VID_MODE_VPG_MODE, dsi), + }; + int i; + + dsi->debugfs_vpg = kmemdup(debugfs, sizeof(debugfs), GFP_KERNEL); + if (!dsi->debugfs_vpg) + return; + + for (i = 0; i < ARRAY_SIZE(debugfs); i++) + debugfs_create_file(dsi->debugfs_vpg[i].name, 0644, + dsi->debugfs, &dsi->debugfs_vpg[i], + &fops_x32); +} + static void dw_mipi_dsi_debugfs_init(struct dw_mipi_dsi *dsi) { dsi->debugfs = debugfs_create_dir(dev_name(dsi->dev), NULL); @@ -972,14 +1082,13 @@ static void dw_mipi_dsi_debugfs_init(struct dw_mipi_dsi *dsi) return; } - debugfs_create_bool("vpg", 0660, dsi->debugfs, &dsi->vpg); - debugfs_create_bool("vpg_horizontal", 0660, dsi->debugfs, - &dsi->vpg_horizontal); + debugfs_create_files(dsi); } static void dw_mipi_dsi_debugfs_remove(struct dw_mipi_dsi *dsi) { debugfs_remove_recursive(dsi->debugfs); + kfree(dsi->debugfs_vpg); } #else diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c index d951cdc58297..2272adcc5b4a 100644 --- a/drivers/gpu/drm/bridge/tc358775.c +++ b/drivers/gpu/drm/bridge/tc358775.c @@ -485,7 +485,7 @@ static void tc_bridge_enable(struct drm_bridge *bridge) val |= TC358775_LVCFG_PCLKDIV(DIVIDE_BY_6); } else { val |= TC358775_LVCFG_PCLKDIV(DIVIDE_BY_3); - }; + } d2l_write(tc->i2c, LVCFG, val); } |