From c60362500149608bc535c9cb80d42ddd03f8152a Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 2 May 2019 15:53:32 -0700 Subject: dt-bindings: drm/bridge/synopsys: dw-hdmi: Add "unwedge" for ddc bus In certain situations it was seen that we could wedge up the DDC bus on the HDMI adapter on rk3288. The only way to unwedge was to mux one of the pins over to GPIO output-driven-low temporarily and then quickly mux back. Full details can be found in the patch ("drm/bridge/synopsys: dw-hdmi: Add "unwedge" for ddc bus"). Since unwedge requires remuxing the pins, we first need to add to the bindings so that we can specify what state the pins should be in for unwedging. Reviewed-by: Rob Herring Signed-off-by: Douglas Anderson Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190502225336.206885-1-dianders@chromium.org --- .../devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt index 39143424a474..8346bac81f1c 100644 --- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt +++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt @@ -38,6 +38,13 @@ Optional properties - phys: from general PHY binding: the phandle for the PHY device. - phy-names: Should be "hdmi" if phys references an external phy. +Optional pinctrl entry: +- If you have both a "unwedge" and "default" pinctrl entry, dw_hdmi + will switch to the unwedge pinctrl state for 10ms if it ever gets an + i2c timeout. It's intended that this unwedge pinctrl entry will + cause the SDA line to be driven low to work around a hardware + errata. + Example: hdmi: hdmi@ff980000 { -- cgit v1.2.3 From e5852bee90d6cb7d9bd2c635c056e7746f137e06 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Sat, 8 Jun 2019 17:26:53 +0200 Subject: drm/fb-helper: Remove drm_fb_helper_connector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All drivers add all their connectors so there's no need to keep around an array of available connectors. Instead we just put the useable (not writeback) connectors in a temporary array using drm_client_for_each_connector_iter() everytime we probe the outputs. Other places where it's necessary to look at the connectors, we just iterate over them using the same iterator function. Rename functions which signature is changed since they will be moved to drm_client in a later patch. v6: Improve commit message (Sam Ravnborg) Signed-off-by: Noralf Trønnes Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20190608152657.36613-2-noralf@tronnes.org --- Documentation/gpu/todo.rst | 4 + drivers/gpu/drm/drm_fb_helper.c | 506 +++++++++++++--------------------------- include/drm/drm_client.h | 15 ++ include/drm/drm_fb_helper.h | 80 ++----- 4 files changed, 197 insertions(+), 408 deletions(-) (limited to 'Documentation') diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 9d4038c50013..ab96ba0600a9 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -292,6 +292,10 @@ drm_fb_helper tasks - The max connector argument for drm_fb_helper_init() and drm_fb_helper_fbdev_setup() isn't used anymore and can be removed. +- The helper doesn't keep an array of connectors anymore so these can be + removed: drm_fb_helper_single_add_all_connectors(), + drm_fb_helper_add_one_connector() and drm_fb_helper_remove_one_connector(). + Core refactorings ================= diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 7b388674a456..b936db6280ac 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -95,12 +95,6 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * Setup fbdev emulation by calling drm_fb_helper_fbdev_setup() and tear it * down by calling drm_fb_helper_fbdev_teardown(). * - * Drivers that need to handle connector hotplugging (e.g. dp mst) can't use - * the setup helper and will need to do the whole four-step setup process with - * drm_fb_helper_prepare(), drm_fb_helper_init(), - * drm_fb_helper_single_add_all_connectors(), enable hotplugging and - * drm_fb_helper_initial_config() to avoid a possible race window. - * * At runtime drivers should restore the fbdev console by using * drm_fb_helper_lastclose() as their &drm_driver.lastclose callback. * They should also notify the fb helper code from updates to the output @@ -123,8 +117,7 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * encoders and connectors. To finish up the fbdev helper initialization, the * drm_fb_helper_init() function is called. To probe for all attached displays * and set up an initial configuration using the detected hardware, drivers - * should call drm_fb_helper_single_add_all_connectors() followed by - * drm_fb_helper_initial_config(). + * should call drm_fb_helper_initial_config(). * * If &drm_framebuffer_funcs.dirty is set, the * drm_fb_helper_{cfb,sys}_{write,fillrect,copyarea,imageblit} functions will @@ -137,165 +130,6 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * deferred I/O (coupled with drm_fb_helper_fbdev_teardown()). */ -#define drm_fb_helper_for_each_connector(fbh, i__) \ - for (({ lockdep_assert_held(&(fbh)->lock); }), \ - i__ = 0; i__ < (fbh)->connector_count; i__++) - -static int __drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - struct drm_fb_helper_connector *fb_conn; - struct drm_fb_helper_connector **temp; - unsigned int count; - - if (!drm_fbdev_emulation) - return 0; - - lockdep_assert_held(&fb_helper->lock); - - count = fb_helper->connector_count + 1; - - if (count > fb_helper->connector_info_alloc_count) { - size_t size = count * sizeof(fb_conn); - - temp = krealloc(fb_helper->connector_info, size, GFP_KERNEL); - if (!temp) - return -ENOMEM; - - fb_helper->connector_info_alloc_count = count; - fb_helper->connector_info = temp; - } - - fb_conn = kzalloc(sizeof(*fb_conn), GFP_KERNEL); - if (!fb_conn) - return -ENOMEM; - - drm_connector_get(connector); - fb_conn->connector = connector; - fb_helper->connector_info[fb_helper->connector_count++] = fb_conn; - - return 0; -} - -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - int err; - - if (!fb_helper) - return 0; - - mutex_lock(&fb_helper->lock); - err = __drm_fb_helper_add_one_connector(fb_helper, connector); - mutex_unlock(&fb_helper->lock); - - return err; -} -EXPORT_SYMBOL(drm_fb_helper_add_one_connector); - -/** - * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev - * emulation helper - * @fb_helper: fbdev initialized with drm_fb_helper_init, can be NULL - * - * This functions adds all the available connectors for use with the given - * fb_helper. This is a separate step to allow drivers to freely assign - * connectors to the fbdev, e.g. if some are reserved for special purposes or - * not adequate to be used for the fbcon. - * - * This function is protected against concurrent connector hotadds/removals - * using drm_fb_helper_add_one_connector() and - * drm_fb_helper_remove_one_connector(). - */ -int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev; - struct drm_connector *connector; - struct drm_connector_list_iter conn_iter; - int i, ret = 0; - - if (!drm_fbdev_emulation || !fb_helper) - return 0; - - dev = fb_helper->dev; - - mutex_lock(&fb_helper->lock); - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) { - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - continue; - - ret = __drm_fb_helper_add_one_connector(fb_helper, connector); - if (ret) - goto fail; - } - goto out; - -fail: - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_fb_helper_connector *fb_helper_connector = - fb_helper->connector_info[i]; - - drm_connector_put(fb_helper_connector->connector); - - kfree(fb_helper_connector); - fb_helper->connector_info[i] = NULL; - } - fb_helper->connector_count = 0; -out: - drm_connector_list_iter_end(&conn_iter); - mutex_unlock(&fb_helper->lock); - - return ret; -} -EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); - -static int __drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - struct drm_fb_helper_connector *fb_helper_connector; - int i, j; - - if (!drm_fbdev_emulation) - return 0; - - lockdep_assert_held(&fb_helper->lock); - - drm_fb_helper_for_each_connector(fb_helper, i) { - if (fb_helper->connector_info[i]->connector == connector) - break; - } - - if (i == fb_helper->connector_count) - return -EINVAL; - fb_helper_connector = fb_helper->connector_info[i]; - drm_connector_put(fb_helper_connector->connector); - - for (j = i + 1; j < fb_helper->connector_count; j++) - fb_helper->connector_info[j - 1] = fb_helper->connector_info[j]; - - fb_helper->connector_count--; - kfree(fb_helper_connector); - - return 0; -} - -int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - int err; - - if (!fb_helper) - return 0; - - mutex_lock(&fb_helper->lock); - err = __drm_fb_helper_remove_one_connector(fb_helper, connector); - mutex_unlock(&fb_helper->lock); - - return err; -} -EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); - static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) { uint16_t *r_base, *g_base, *b_base; @@ -645,20 +479,9 @@ int drm_fb_helper_init(struct drm_device *dev, return ret; } - fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); - if (!fb_helper->connector_info) - goto out_free; - - fb_helper->connector_info_alloc_count = dev->mode_config.num_connector; - fb_helper->connector_count = 0; - dev->fb_helper = fb_helper; return 0; -out_free: - drm_client_release(&fb_helper->client); - - return -ENOMEM; } EXPORT_SYMBOL(drm_fb_helper_init); @@ -733,7 +556,6 @@ EXPORT_SYMBOL(drm_fb_helper_unregister_fbi); void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) { struct fb_info *info; - int i; if (!fb_helper) return; @@ -766,12 +588,6 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) if (!fb_helper->client.funcs) drm_client_release(&fb_helper->client); - - for (i = 0; i < fb_helper->connector_count; i++) { - drm_connector_put(fb_helper->connector_info[i]->connector); - kfree(fb_helper->connector_info[i]); - } - kfree(fb_helper->connector_info); } EXPORT_SYMBOL(drm_fb_helper_fini); @@ -1650,8 +1466,9 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, struct drm_client_dev *client = &fb_helper->client; int ret = 0; int crtc_count = 0; - int i; + struct drm_connector_list_iter conn_iter; struct drm_fb_helper_surface_size sizes; + struct drm_connector *connector; struct drm_mode_set *mode_set; int best_depth = 0; @@ -1668,11 +1485,11 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, if (preferred_bpp != sizes.surface_bpp) sizes.surface_depth = sizes.surface_bpp = preferred_bpp; - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; + drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { struct drm_cmdline_mode *cmdline_mode; - cmdline_mode = &fb_helper_conn->connector->cmdline_mode; + cmdline_mode = &connector->cmdline_mode; if (cmdline_mode->bpp_specified) { switch (cmdline_mode->bpp) { @@ -1697,6 +1514,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, break; } } + drm_connector_list_iter_end(&conn_iter); /* * If we run into a situation where, for example, the primary plane @@ -1881,26 +1699,12 @@ void drm_fb_helper_fill_info(struct fb_info *info, } EXPORT_SYMBOL(drm_fb_helper_fill_info); -static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper, - uint32_t maxX, - uint32_t maxY) -{ - struct drm_connector *connector; - int i, count = 0; - - drm_fb_helper_for_each_connector(fb_helper, i) { - connector = fb_helper->connector_info[i]->connector; - count += connector->funcs->fill_modes(connector, maxX, maxY); - } - - return count; -} - -struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) +static struct drm_display_mode * +drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int height) { struct drm_display_mode *mode; - list_for_each_entry(mode, &fb_connector->connector->modes, head) { + list_for_each_entry(mode, &connector->modes, head) { if (mode->hdisplay > width || mode->vdisplay > height) continue; @@ -1909,20 +1713,15 @@ struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector * } return NULL; } -EXPORT_SYMBOL(drm_has_preferred_mode); - -static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) -{ - return fb_connector->connector->cmdline_mode.specified; -} -struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn) +static struct drm_display_mode * +drm_connector_pick_cmdline_mode(struct drm_connector *connector) { struct drm_cmdline_mode *cmdline_mode; struct drm_display_mode *mode; bool prefer_non_interlace; - cmdline_mode = &fb_helper_conn->connector->cmdline_mode; + cmdline_mode = &connector->cmdline_mode; if (cmdline_mode->specified == false) return NULL; @@ -1934,7 +1733,7 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f prefer_non_interlace = !cmdline_mode->interlace; again: - list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { + list_for_each_entry(mode, &connector->modes, head) { /* check width/height */ if (mode->hdisplay != cmdline_mode->xres || mode->vdisplay != cmdline_mode->yres) @@ -1961,12 +1760,11 @@ again: } create_mode: - mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, - cmdline_mode); - list_add(&mode->head, &fb_helper_conn->connector->modes); + mode = drm_mode_create_from_cmdline_mode(connector->dev, cmdline_mode); + list_add(&mode->head, &connector->modes); + return mode; } -EXPORT_SYMBOL(drm_pick_cmdline_mode); static bool drm_connector_enabled(struct drm_connector *connector, bool strict) { @@ -1983,15 +1781,16 @@ static bool drm_connector_enabled(struct drm_connector *connector, bool strict) return enable; } -static void drm_enable_connectors(struct drm_fb_helper *fb_helper, - bool *enabled) +static void drm_client_connectors_enabled(struct drm_connector **connectors, + unsigned int connector_count, + bool *enabled) { bool any_enabled = false; struct drm_connector *connector; int i = 0; - drm_fb_helper_for_each_connector(fb_helper, i) { - connector = fb_helper->connector_info[i]->connector; + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; enabled[i] = drm_connector_enabled(connector, true); DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no"); @@ -2002,28 +1801,27 @@ static void drm_enable_connectors(struct drm_fb_helper *fb_helper, if (any_enabled) return; - drm_fb_helper_for_each_connector(fb_helper, i) { - connector = fb_helper->connector_info[i]->connector; - enabled[i] = drm_connector_enabled(connector, false); - } + for (i = 0; i < connector_count; i++) + enabled[i] = drm_connector_enabled(connectors[i], false); } -static bool drm_target_cloned(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) +static bool drm_client_target_cloned(struct drm_device *dev, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_fb_offset *offsets, + bool *enabled, int width, int height) { int count, i, j; bool can_clone = false; - struct drm_fb_helper_connector *fb_helper_conn; struct drm_display_mode *dmt_mode, *mode; /* only contemplate cloning in the single crtc case */ - if (fb_helper->dev->mode_config.num_crtc > 1) + if (dev->mode_config.num_crtc > 1) return false; count = 0; - drm_fb_helper_for_each_connector(fb_helper, i) { + for (i = 0; i < connector_count; i++) { if (enabled[i]) count++; } @@ -2034,11 +1832,10 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper, /* check the command line or if nothing common pick 1024x768 */ can_clone = true; - drm_fb_helper_for_each_connector(fb_helper, i) { + for (i = 0; i < connector_count; i++) { if (!enabled[i]) continue; - fb_helper_conn = fb_helper->connector_info[i]; - modes[i] = drm_pick_cmdline_mode(fb_helper_conn); + modes[i] = drm_connector_pick_cmdline_mode(connectors[i]); if (!modes[i]) { can_clone = false; break; @@ -2062,14 +1859,13 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper, /* try and find a 1024x768 mode on each connector */ can_clone = true; - dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false); + dmt_mode = drm_mode_find_dmt(dev, 1024, 768, 60, false); - drm_fb_helper_for_each_connector(fb_helper, i) { + for (i = 0; i < connector_count; i++) { if (!enabled[i]) continue; - fb_helper_conn = fb_helper->connector_info[i]; - list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { + list_for_each_entry(mode, &connectors[i]->modes, head) { if (drm_mode_match(mode, dmt_mode, DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_CLOCK | @@ -2089,30 +1885,31 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper, return false; } -static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - int idx, - int h_idx, int v_idx) +static int drm_client_get_tile_offsets(struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_fb_offset *offsets, + int idx, + int h_idx, int v_idx) { - struct drm_fb_helper_connector *fb_helper_conn; + struct drm_connector *connector; int i; int hoffset = 0, voffset = 0; - drm_fb_helper_for_each_connector(fb_helper, i) { - fb_helper_conn = fb_helper->connector_info[i]; - if (!fb_helper_conn->connector->has_tile) + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; + if (!connector->has_tile) continue; if (!modes[i] && (h_idx || v_idx)) { DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i, - fb_helper_conn->connector->base.id); + connector->base.id); continue; } - if (fb_helper_conn->connector->tile_h_loc < h_idx) + if (connector->tile_h_loc < h_idx) hoffset += modes[i]->hdisplay; - if (fb_helper_conn->connector->tile_v_loc < v_idx) + if (connector->tile_v_loc < v_idx) voffset += modes[i]->vdisplay; } offsets[idx].x = hoffset; @@ -2121,20 +1918,21 @@ static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper, return 0; } -static bool drm_target_preferred(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) +static bool drm_client_target_preferred(struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_fb_offset *offsets, + bool *enabled, int width, int height) { - struct drm_fb_helper_connector *fb_helper_conn; - const u64 mask = BIT_ULL(fb_helper->connector_count) - 1; + const u64 mask = BIT_ULL(connector_count) - 1; + struct drm_connector *connector; u64 conn_configured = 0; int tile_pass = 0; int i; retry: - drm_fb_helper_for_each_connector(fb_helper, i) { - fb_helper_conn = fb_helper->connector_info[i]; + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; if (conn_configured & BIT_ULL(i)) continue; @@ -2145,17 +1943,17 @@ retry: } /* first pass over all the untiled connectors */ - if (tile_pass == 0 && fb_helper_conn->connector->has_tile) + if (tile_pass == 0 && connector->has_tile) continue; if (tile_pass == 1) { - if (fb_helper_conn->connector->tile_h_loc != 0 || - fb_helper_conn->connector->tile_v_loc != 0) + if (connector->tile_h_loc != 0 || + connector->tile_v_loc != 0) continue; } else { - if (fb_helper_conn->connector->tile_h_loc != tile_pass - 1 && - fb_helper_conn->connector->tile_v_loc != tile_pass - 1) + if (connector->tile_h_loc != tile_pass - 1 && + connector->tile_v_loc != tile_pass - 1) /* if this tile_pass doesn't cover any of the tiles - keep going */ continue; @@ -2163,22 +1961,22 @@ retry: * find the tile offsets for this pass - need to find * all tiles left and above */ - drm_get_tile_offsets(fb_helper, modes, offsets, - i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc); + drm_client_get_tile_offsets(connectors, connector_count, modes, offsets, i, + connector->tile_h_loc, connector->tile_v_loc); } DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", - fb_helper_conn->connector->base.id); + connector->base.id); /* got for command line mode first */ - modes[i] = drm_pick_cmdline_mode(fb_helper_conn); + modes[i] = drm_connector_pick_cmdline_mode(connector); if (!modes[i]) { DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", - fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0); - modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); + connector->base.id, connector->tile_group ? connector->tile_group->id : 0); + modes[i] = drm_connector_has_preferred_mode(connector, width, height); } /* No preferred modes, pick one off the list */ - if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) { - list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head) + if (!modes[i] && !list_empty(&connector->modes)) { + list_for_each_entry(modes[i], &connector->modes, head) break; } DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : @@ -2207,40 +2005,41 @@ static bool connector_has_possible_crtc(struct drm_connector *connector, return false; } -static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, - struct drm_crtc **best_crtcs, - struct drm_display_mode **modes, - int n, int width, int height) +static int drm_client_pick_crtcs(struct drm_client_dev *client, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_crtc **best_crtcs, + struct drm_display_mode **modes, + int n, int width, int height) { - struct drm_client_dev *client = &fb_helper->client; + struct drm_device *dev = client->dev; struct drm_connector *connector; int my_score, best_score, score; struct drm_crtc **crtcs, *crtc; struct drm_mode_set *modeset; - struct drm_fb_helper_connector *fb_helper_conn; int o; - if (n == fb_helper->connector_count) + if (n == connector_count) return 0; - fb_helper_conn = fb_helper->connector_info[n]; - connector = fb_helper_conn->connector; + connector = connectors[n]; best_crtcs[n] = NULL; - best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); + best_score = drm_client_pick_crtcs(client, connectors, connector_count, + best_crtcs, modes, n + 1, width, height); if (modes[n] == NULL) return best_score; - crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL); + crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); if (!crtcs) return best_score; my_score = 1; if (connector->status == connector_status_connected) my_score++; - if (drm_has_cmdline_mode(fb_helper_conn)) + if (connector->cmdline_mode.specified) my_score++; - if (drm_has_preferred_mode(fb_helper_conn, width, height)) + if (drm_connector_has_preferred_mode(connector, width, height)) my_score++; /* @@ -2259,7 +2058,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, if (o < n) { /* ignore cloning unless only a single crtc */ - if (fb_helper->dev->mode_config.num_crtc > 1) + if (dev->mode_config.num_crtc > 1) continue; if (!drm_mode_equal(modes[o], modes[n])) @@ -2268,12 +2067,11 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, crtcs[n] = crtc; memcpy(crtcs, best_crtcs, n * sizeof(*crtcs)); - score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, - width, height); + score = my_score + drm_client_pick_crtcs(client, connectors, connector_count, + crtcs, modes, n + 1, width, height); if (score > best_score) { best_score = score; - memcpy(best_crtcs, crtcs, - fb_helper->connector_count * sizeof(*crtcs)); + memcpy(best_crtcs, crtcs, connector_count * sizeof(*crtcs)); } } @@ -2282,15 +2080,17 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, } /* Try to read the BIOS display configuration and use it for the initial config */ -static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper, - struct drm_crtc **crtcs, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) -{ - struct drm_device *dev = fb_helper->dev; - unsigned int count = min(fb_helper->connector_count, BITS_PER_LONG); +static bool drm_client_firmware_config(struct drm_client_dev *client, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_crtc **crtcs, + struct drm_display_mode **modes, + struct drm_fb_offset *offsets, + bool *enabled, int width, int height) +{ + unsigned int count = min_t(unsigned int, connector_count, BITS_PER_LONG); unsigned long conn_configured, conn_seq, mask; + struct drm_device *dev = client->dev; int i, j; bool *save_enabled; bool fallback = true, ret = true; @@ -2316,13 +2116,11 @@ static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper, retry: conn_seq = conn_configured; for (i = 0; i < count; i++) { - struct drm_fb_helper_connector *fb_conn; struct drm_connector *connector; struct drm_encoder *encoder; struct drm_crtc *new_crtc; - fb_conn = fb_helper->connector_info[i]; - connector = fb_conn->connector; + connector = connectors[i]; if (conn_configured & BIT(i)) continue; @@ -2379,14 +2177,13 @@ retry: connector->name); /* go for command line mode first */ - modes[i] = drm_pick_cmdline_mode(fb_conn); + modes[i] = drm_connector_pick_cmdline_mode(connector); /* try for preferred next */ if (!modes[i]) { DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n", connector->name, connector->has_tile); - modes[i] = drm_has_preferred_mode(fb_conn, width, - height); + modes[i] = drm_connector_has_preferred_mode(connector, width, height); } /* No preferred mode marked by the EDID? Are there any modes? */ @@ -2461,8 +2258,12 @@ bail: static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, u32 width, u32 height) { + struct drm_connector *connector, **connectors = NULL; struct drm_client_dev *client = &fb_helper->client; + struct drm_connector_list_iter conn_iter; struct drm_device *dev = fb_helper->dev; + unsigned int total_modes_count = 0; + unsigned int connector_count = 0; struct drm_display_mode **modes; struct drm_fb_offset *offsets; struct drm_crtc **crtcs; @@ -2470,16 +2271,28 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, int i; DRM_DEBUG_KMS("\n"); - /* prevent concurrent modification of connector_count by hotplug */ - lockdep_assert_held(&fb_helper->lock); - - crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL); - modes = kcalloc(fb_helper->connector_count, - sizeof(struct drm_display_mode *), GFP_KERNEL); - offsets = kcalloc(fb_helper->connector_count, - sizeof(struct drm_fb_offset), GFP_KERNEL); - enabled = kcalloc(fb_helper->connector_count, - sizeof(bool), GFP_KERNEL); + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { + struct drm_connector **tmp; + + tmp = krealloc(connectors, (connector_count + 1) * sizeof(*connectors), GFP_KERNEL); + if (!tmp) + goto free_connectors; + + connectors = tmp; + drm_connector_get(connector); + connectors[connector_count++] = connector; + } + drm_connector_list_iter_end(&conn_iter); + + if (!connector_count) + return; + + crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); + modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL); + offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL); + enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL); if (!crtcs || !modes || !enabled || !offsets) { DRM_ERROR("Memory allocation failed\n"); goto out; @@ -2488,40 +2301,42 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, mutex_lock(&client->modeset_mutex); mutex_lock(&fb_helper->dev->mode_config.mutex); - if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0) + for (i = 0; i < connector_count; i++) + total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height); + if (!total_modes_count) DRM_DEBUG_KMS("No connectors reported connected with modes\n"); - drm_enable_connectors(fb_helper, enabled); - - if (!drm_fb_helper_firmware_config(fb_helper, crtcs, modes, offsets, - enabled, width, height)) { - memset(modes, 0, fb_helper->connector_count*sizeof(modes[0])); - memset(crtcs, 0, fb_helper->connector_count*sizeof(crtcs[0])); - memset(offsets, 0, fb_helper->connector_count*sizeof(offsets[0])); - - if (!drm_target_cloned(fb_helper, modes, offsets, - enabled, width, height) && - !drm_target_preferred(fb_helper, modes, offsets, - enabled, width, height)) + drm_client_connectors_enabled(connectors, connector_count, enabled); + + if (!drm_client_firmware_config(client, connectors, connector_count, crtcs, + modes, offsets, enabled, width, height)) { + memset(modes, 0, connector_count * sizeof(*modes)); + memset(crtcs, 0, connector_count * sizeof(*crtcs)); + memset(offsets, 0, connector_count * sizeof(*offsets)); + + if (!drm_client_target_cloned(dev, connectors, connector_count, modes, + offsets, enabled, width, height) && + !drm_client_target_preferred(connectors, connector_count, modes, + offsets, enabled, width, height)) DRM_ERROR("Unable to find initial modes\n"); DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); - drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); + drm_client_pick_crtcs(client, connectors, connector_count, + crtcs, modes, 0, width, height); } mutex_unlock(&fb_helper->dev->mode_config.mutex); drm_client_modeset_release(client); - drm_fb_helper_for_each_connector(fb_helper, i) { + for (i = 0; i < connector_count; i++) { struct drm_display_mode *mode = modes[i]; struct drm_crtc *crtc = crtcs[i]; struct drm_fb_offset *offset = &offsets[i]; if (mode && crtc) { struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc); - struct drm_connector *connector = - fb_helper->connector_info[i]->connector; + struct drm_connector *connector = connectors[i]; DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", mode->name, crtc->base.id, offset->x, offset->y); @@ -2544,6 +2359,10 @@ out: kfree(modes); kfree(offsets); kfree(enabled); +free_connectors: + for (i = 0; i < connector_count; i++) + drm_connector_put(connectors[i]); + kfree(connectors); } /* @@ -2556,10 +2375,11 @@ out: static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) { struct drm_client_dev *client = &fb_helper->client; + struct drm_connector_list_iter conn_iter; struct fb_info *info = fb_helper->fbdev; unsigned int rotation, sw_rotations = 0; + struct drm_connector *connector; struct drm_mode_set *modeset; - int i; mutex_lock(&client->modeset_mutex); drm_client_for_each_modeset(modeset, client) { @@ -2576,10 +2396,8 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) } mutex_unlock(&client->modeset_mutex); - mutex_lock(&fb_helper->dev->mode_config.mutex); - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_connector *connector = - fb_helper->connector_info[i]->connector; + drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { /* use first connected connector for the physical dimensions */ if (connector->status == connector_status_connected) { @@ -2588,7 +2406,7 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) break; } } - mutex_unlock(&fb_helper->dev->mode_config.mutex); + drm_connector_list_iter_end(&conn_iter); switch (sw_rotations) { case DRM_MODE_ROTATE_0: @@ -2826,12 +2644,6 @@ int drm_fb_helper_fbdev_setup(struct drm_device *dev, return ret; } - ret = drm_fb_helper_single_add_all_connectors(fb_helper); - if (ret < 0) { - DRM_DEV_ERROR(dev->dev, "fbdev: Failed to add connectors (ret=%d)\n", ret); - goto err_drm_fb_helper_fini; - } - if (!drm_drv_uses_atomic_modeset(dev)) drm_helper_disable_unused_functions(dev); @@ -3137,10 +2949,6 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client) if (ret) goto err; - ret = drm_fb_helper_single_add_all_connectors(fb_helper); - if (ret) - goto err_cleanup; - if (!drm_drv_uses_atomic_modeset(dev)) drm_helper_disable_unused_functions(dev); diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 6cf48419f77f..8d94880bbe25 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -7,6 +7,7 @@ #include #include +#include #include struct drm_client_dev; @@ -169,6 +170,20 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode); for (({ lockdep_assert_held(&(client)->modeset_mutex); }), \ modeset = (client)->modesets; modeset->crtc; modeset++) +/** + * drm_client_for_each_connector_iter - connector_list iterator macro + * @connector: &struct drm_connector pointer used as cursor + * @iter: &struct drm_connector_list_iter + * + * This iterates the connectors that are useable for internal clients (excludes + * writeback connectors). + * + * For more info see drm_for_each_connector_iter(). + */ +#define drm_client_for_each_connector_iter(connector, iter) \ + drm_for_each_connector_iter(connector, iter) \ + if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) + int drm_client_debugfs_init(struct drm_minor *minor); #endif diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 6b334f4d8a22..e14d52ef9638 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -97,16 +97,10 @@ struct drm_fb_helper_funcs { struct drm_fb_helper_surface_size *sizes); }; -struct drm_fb_helper_connector { - struct drm_connector *connector; -}; - /** * struct drm_fb_helper - main structure to emulate fbdev on top of KMS * @fb: Scanout framebuffer object * @dev: DRM device - * @connector_count: number of connected connectors - * @connector_info_alloc_count: size of connector_info * @funcs: driver callbacks for fb helper * @fbdev: emulated fbdev device info struct * @pseudo_palette: fake palette of 16 colors @@ -138,15 +132,6 @@ struct drm_fb_helper { struct drm_framebuffer *fb; struct drm_device *dev; - int connector_count; - int connector_info_alloc_count; - /** - * @connector_info: - * - * Array of per-connector information. Do not iterate directly, but use - * drm_fb_helper_for_each_connector. - */ - struct drm_fb_helper_connector **connector_info; const struct drm_fb_helper_funcs *funcs; struct fb_info *fbdev; u32 pseudo_palette[17]; @@ -286,18 +271,8 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper); int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel); -int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper); int drm_fb_helper_debug_enter(struct fb_info *info); int drm_fb_helper_debug_leave(struct fb_info *info); -struct drm_display_mode * -drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, - int width, int height); -struct drm_display_mode * -drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn); - -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector); -int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector); int drm_fb_helper_fbdev_setup(struct drm_device *dev, struct drm_fb_helper *fb_helper, @@ -472,12 +447,6 @@ static inline int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, return 0; } -static inline int -drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) -{ - return 0; -} - static inline int drm_fb_helper_debug_enter(struct fb_info *info) { return 0; @@ -488,34 +457,6 @@ static inline int drm_fb_helper_debug_leave(struct fb_info *info) return 0; } -static inline struct drm_display_mode * -drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, - int width, int height) -{ - return NULL; -} - -static inline struct drm_display_mode * -drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, - int width, int height) -{ - return NULL; -} - -static inline int -drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - return 0; -} - -static inline int -drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - return 0; -} - static inline int drm_fb_helper_fbdev_setup(struct drm_device *dev, struct drm_fb_helper *fb_helper, @@ -557,6 +498,27 @@ drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) #endif +/* TODO: There's a todo entry to remove these three */ +static inline int +drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) +{ + return 0; +} + +static inline int +drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, + struct drm_connector *connector) +{ + return 0; +} + +static inline int +drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, + struct drm_connector *connector) +{ + return 0; +} + /** * drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffers * @a: memory range, users of which are to be removed -- cgit v1.2.3 From ce25600842f3f45587344301e09d0a92549335d0 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Sat, 8 Jun 2019 17:26:57 +0200 Subject: drm/todo: Add bootsplash entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ease entry for anyone wanting to pick up the bootsplash work by providing a couple of pointers. v2: Add Sam as contact (Sam) Signed-off-by: Noralf Trønnes Reviewed-by: Paul Kocialkowski Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20190608152657.36613-6-noralf@tronnes.org --- Documentation/gpu/todo.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'Documentation') diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index ab96ba0600a9..b4a76c2703e5 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -484,5 +484,20 @@ i915 device_link_add to model the dependency between i915 and snd_had. See https://dri.freedesktop.org/docs/drm/driver-api/device_link.html +Bootsplash +========== + +There is support in place now for writing internal DRM clients making it +possible to pick up the bootsplash work that was rejected because it was written +for fbdev. + +- [v6,8/8] drm/client: Hack: Add bootsplash example + https://patchwork.freedesktop.org/patch/306579/ + +- [RFC PATCH v2 00/13] Kernel based bootsplash + https://lkml.org/lkml/2017/12/13/764 + +Contact: Sam Ravnborg + Outside DRM =========== -- cgit v1.2.3 From 1452c25b0e60278820f3d2155c65f1bfcce5ee79 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Wed, 12 Jun 2019 10:50:19 -0400 Subject: drm: Add helpers to kick off self refresh mode in drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a new drm helper library to help drivers implement self refresh. Drivers choosing to use it will register crtcs and will receive callbacks when it's time to enter or exit self refresh mode. In its current form, it has a timer which will trigger after a driver-specified amount of inactivity. When the timer triggers, the helpers will submit a new atomic commit to shut the refreshing pipe off. On the next atomic commit, the drm core will revert the self refresh state and bring everything back up to be actively driven. From the driver's perspective, this works like a regular disable/enable cycle. The driver need only check the 'self_refresh_active' state in crtc_state. It should initiate self refresh mode on the panel and enter an off or low-power state. Changes in v2: - s/psr/self_refresh/ (Daniel) - integrated the psr exit into the commit that wakes it up (Jose/Daniel) - made the psr state per-crtc (Jose/Daniel) Changes in v3: - Remove the self_refresh_(active|changed) from connector state (Daniel) - Simplify loop in drm_self_refresh_helper_alter_state (Daniel) - Improve self_refresh_aware comment (Daniel) - s/self_refresh_state/self_refresh_data/ (Daniel) Changes in v4: - Move docbook location below panel (Daniel) - Improve docbook with references and more detailed explanation (Daniel) - Instead of register/unregister, use init/cleanup (Daniel) Changes in v5: - Resolved conflict in drm_atomic_helper.c #include block - Resolved conflict in rst with HDCP helper docs Changes in v6: - Fix include ordering, clean up forward declarations (Sam) Link to v1: https://patchwork.freedesktop.org/patch/msgid/20190228210939.83386-2-sean@poorly.run Link to v2: https://patchwork.freedesktop.org/patch/msgid/20190326204509.96515-1-sean@poorly.run Link to v3: https://patchwork.freedesktop.org/patch/msgid/20190502194956.218441-6-sean@poorly.run Link to v4: https://patchwork.freedesktop.org/patch/msgid/20190508160920.144739-6-sean@poorly.run Link to v5: https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-6-sean@poorly.run Cc: Daniel Vetter Cc: Jose Souza Cc: Zain Wang Cc: Tomasz Figa Cc: Ville Syrjälä Cc: Sam Ravnborg Tested-by: Heiko Stuebner Reviewed-by: Daniel Vetter Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190612145026.191846-1-sean@poorly.run --- Documentation/gpu/drm-kms-helpers.rst | 9 ++ drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_atomic.c | 2 + drivers/gpu/drm/drm_atomic_helper.c | 35 ++++- drivers/gpu/drm/drm_atomic_state_helper.c | 4 + drivers/gpu/drm/drm_atomic_uapi.c | 7 +- drivers/gpu/drm/drm_self_refresh_helper.c | 216 ++++++++++++++++++++++++++++++ include/drm/drm_atomic.h | 15 +++ include/drm/drm_connector.h | 14 ++ include/drm/drm_crtc.h | 19 +++ include/drm/drm_self_refresh_helper.h | 20 +++ 11 files changed, 338 insertions(+), 5 deletions(-) create mode 100644 drivers/gpu/drm/drm_self_refresh_helper.c create mode 100644 include/drm/drm_self_refresh_helper.h (limited to 'Documentation') diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 0fe726a6ee67..b327bbc11182 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -181,6 +181,15 @@ Panel Helper Reference .. kernel-doc:: drivers/gpu/drm/drm_panel_orientation_quirks.c :export: +Panel Self Refresh Helper Reference +=================================== + +.. kernel-doc:: drivers/gpu/drm/drm_self_refresh_helper.c + :doc: overview + +.. kernel-doc:: drivers/gpu/drm/drm_self_refresh_helper.c + :export: + HDCP Helper Functions Reference =============================== diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index d36feb4a6233..9d630a28a788 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -43,7 +43,7 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_dsc.o drm_probe_helper drm_simple_kms_helper.o drm_modeset_helper.o \ drm_scdc_helper.o drm_gem_framebuffer_helper.o \ drm_atomic_state_helper.o drm_damage_helper.o \ - drm_format_helper.o + drm_format_helper.o drm_self_refresh_helper.o drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index a4e779deab0f..419381abbdd1 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -384,6 +384,7 @@ static void drm_atomic_crtc_print_state(struct drm_printer *p, drm_printf(p, "crtc[%u]: %s\n", crtc->base.id, crtc->name); drm_printf(p, "\tenable=%d\n", state->enable); drm_printf(p, "\tactive=%d\n", state->active); + drm_printf(p, "\tself_refresh_active=%d\n", state->self_refresh_active); drm_printf(p, "\tplanes_changed=%d\n", state->planes_changed); drm_printf(p, "\tmode_changed=%d\n", state->mode_changed); drm_printf(p, "\tactive_changed=%d\n", state->active_changed); @@ -999,6 +1000,7 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, drm_printf(p, "connector[%u]: %s\n", connector->base.id, connector->name); drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)"); + drm_printf(p, "\tself_refresh_aware=%d\n", state->self_refresh_aware); if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) if (state->writeback_job && state->writeback_job->fb) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index e58be6996069..d9ea7e51ffdc 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -953,10 +954,33 @@ int drm_atomic_helper_check(struct drm_device *dev, if (state->legacy_cursor_update) state->async_update = !drm_atomic_helper_async_check(dev, state); + drm_self_refresh_helper_alter_state(state); + return ret; } EXPORT_SYMBOL(drm_atomic_helper_check); +static bool +crtc_needs_disable(struct drm_crtc_state *old_state, + struct drm_crtc_state *new_state) +{ + /* + * No new_state means the crtc is off, so the only criteria is whether + * it's currently active or in self refresh mode. + */ + if (!new_state) + return drm_atomic_crtc_effectively_active(old_state); + + /* + * We need to run through the crtc_funcs->disable() function if the crtc + * is currently on, if it's transitioning to self refresh mode, or if + * it's in self refresh mode and needs to be fully disabled. + */ + return old_state->active || + (old_state->self_refresh_active && !new_state->enable) || + new_state->self_refresh_active; +} + static void disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) { @@ -977,7 +1001,14 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) old_crtc_state = drm_atomic_get_old_crtc_state(old_state, old_conn_state->crtc); - if (!old_crtc_state->active || + if (new_conn_state->crtc) + new_crtc_state = drm_atomic_get_new_crtc_state( + old_state, + new_conn_state->crtc); + else + new_crtc_state = NULL; + + if (!crtc_needs_disable(old_crtc_state, new_crtc_state) || !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state)) continue; @@ -1023,7 +1054,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) continue; - if (!old_crtc_state->active) + if (!crtc_needs_disable(old_crtc_state, new_crtc_state)) continue; funcs = crtc->helper_private; diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 97ab26679b96..7d7347a6f194 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -129,6 +129,10 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, state->commit = NULL; state->event = NULL; state->pageflip_flags = 0; + + /* Self refresh should be canceled when a new update is available */ + state->active = drm_atomic_crtc_effectively_active(state); + state->self_refresh_active = false; } EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index eb22e8bdd853..abe38bdf85ae 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -490,7 +490,7 @@ drm_atomic_crtc_get_property(struct drm_crtc *crtc, struct drm_mode_config *config = &dev->mode_config; if (property == config->prop_active) - *val = state->active; + *val = drm_atomic_crtc_effectively_active(state); else if (property == config->prop_mode_id) *val = (state->mode_blob) ? state->mode_blob->base.id : 0; else if (property == config->prop_vrr_enabled) @@ -788,7 +788,10 @@ drm_atomic_connector_get_property(struct drm_connector *connector, if (property == config->prop_crtc_id) { *val = (state->crtc) ? state->crtc->base.id : 0; } else if (property == config->dpms_property) { - *val = connector->dpms; + if (state->crtc && state->crtc->state->self_refresh_active) + *val = DRM_MODE_DPMS_ON; + else + *val = connector->dpms; } else if (property == config->tv_select_subconnector_property) { *val = state->tv.subconnector; } else if (property == config->tv_left_margin_property) { diff --git a/drivers/gpu/drm/drm_self_refresh_helper.c b/drivers/gpu/drm/drm_self_refresh_helper.c new file mode 100644 index 000000000000..2b3daaf77841 --- /dev/null +++ b/drivers/gpu/drm/drm_self_refresh_helper.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2019 Google, Inc. + * + * Authors: + * Sean Paul + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * DOC: overview + * + * This helper library provides an easy way for drivers to leverage the atomic + * framework to implement panel self refresh (SR) support. Drivers are + * responsible for initializing and cleaning up the SR helpers on load/unload + * (see &drm_self_refresh_helper_init/&drm_self_refresh_helper_cleanup). + * The connector is responsible for setting + * &drm_connector_state.self_refresh_aware to true at runtime if it is SR-aware + * (meaning it knows how to initiate self refresh on the panel). + * + * Once a crtc has enabled SR using &drm_self_refresh_helper_init, the + * helpers will monitor activity and call back into the driver to enable/disable + * SR as appropriate. The best way to think about this is that it's a DPMS + * on/off request with &drm_crtc_state.self_refresh_active set in crtc state + * that tells you to disable/enable SR on the panel instead of power-cycling it. + * + * During SR, drivers may choose to fully disable their crtc/encoder/bridge + * hardware (in which case no driver changes are necessary), or they can inspect + * &drm_crtc_state.self_refresh_active if they want to enter low power mode + * without full disable (in case full disable/enable is too slow). + * + * SR will be deactivated if there are any atomic updates affecting the + * pipe that is in SR mode. If a crtc is driving multiple connectors, all + * connectors must be SR aware and all will enter/exit SR mode at the same time. + * + * If the crtc and connector are SR aware, but the panel connected does not + * support it (or is otherwise unable to enter SR), the driver should fail + * atomic_check when &drm_crtc_state.self_refresh_active is true. + */ + +struct drm_self_refresh_data { + struct drm_crtc *crtc; + struct delayed_work entry_work; + struct drm_atomic_state *save_state; + unsigned int entry_delay_ms; +}; + +static void drm_self_refresh_helper_entry_work(struct work_struct *work) +{ + struct drm_self_refresh_data *sr_data = container_of( + to_delayed_work(work), + struct drm_self_refresh_data, entry_work); + struct drm_crtc *crtc = sr_data->crtc; + struct drm_device *dev = crtc->dev; + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; + struct drm_connector *conn; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + int i, ret; + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_atomic_state_alloc(dev); + if (!state) { + ret = -ENOMEM; + goto out; + } + +retry: + state->acquire_ctx = &ctx; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto out; + } + + if (!crtc_state->enable) + goto out; + + ret = drm_atomic_add_affected_connectors(state, crtc); + if (ret) + goto out; + + for_each_new_connector_in_state(state, conn, conn_state, i) { + if (!conn_state->self_refresh_aware) + goto out; + } + + crtc_state->active = false; + crtc_state->self_refresh_active = true; + + ret = drm_atomic_commit(state); + if (ret) + goto out; + +out: + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry; + } + + drm_atomic_state_put(state); + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +/** + * drm_self_refresh_helper_alter_state - Alters the atomic state for SR exit + * @state: the state currently being checked + * + * Called at the end of atomic check. This function checks the state for flags + * incompatible with self refresh exit and changes them. This is a bit + * disingenuous since userspace is expecting one thing and we're giving it + * another. However in order to keep self refresh entirely hidden from + * userspace, this is required. + * + * At the end, we queue up the self refresh entry work so we can enter PSR after + * the desired delay. + */ +void drm_self_refresh_helper_alter_state(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + int i; + + if (state->async_update || !state->allow_modeset) { + for_each_old_crtc_in_state(state, crtc, crtc_state, i) { + if (crtc_state->self_refresh_active) { + state->async_update = false; + state->allow_modeset = true; + break; + } + } + } + + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + struct drm_self_refresh_data *sr_data; + + /* Don't trigger the entry timer when we're already in SR */ + if (crtc_state->self_refresh_active) + continue; + + sr_data = crtc->self_refresh_data; + if (!sr_data) + continue; + + mod_delayed_work(system_wq, &sr_data->entry_work, + msecs_to_jiffies(sr_data->entry_delay_ms)); + } +} +EXPORT_SYMBOL(drm_self_refresh_helper_alter_state); + +/** + * drm_self_refresh_helper_init - Initializes self refresh helpers for a crtc + * @crtc: the crtc which supports self refresh supported displays + * @entry_delay_ms: amount of inactivity to wait before entering self refresh + * + * Returns zero if successful or -errno on failure + */ +int drm_self_refresh_helper_init(struct drm_crtc *crtc, + unsigned int entry_delay_ms) +{ + struct drm_self_refresh_data *sr_data = crtc->self_refresh_data; + + /* Helper is already initialized */ + if (WARN_ON(sr_data)) + return -EINVAL; + + sr_data = kzalloc(sizeof(*sr_data), GFP_KERNEL); + if (!sr_data) + return -ENOMEM; + + INIT_DELAYED_WORK(&sr_data->entry_work, + drm_self_refresh_helper_entry_work); + sr_data->entry_delay_ms = entry_delay_ms; + sr_data->crtc = crtc; + + crtc->self_refresh_data = sr_data; + return 0; +} +EXPORT_SYMBOL(drm_self_refresh_helper_init); + +/** + * drm_self_refresh_helper_cleanup - Cleans up self refresh helpers for a crtc + * @crtc: the crtc to cleanup + */ +void drm_self_refresh_helper_cleanup(struct drm_crtc *crtc) +{ + struct drm_self_refresh_data *sr_data = crtc->self_refresh_data; + + /* Helper is already uninitialized */ + if (sr_data) + return; + + crtc->self_refresh_data = NULL; + + cancel_delayed_work_sync(&sr_data->entry_work); + kfree(sr_data); +} +EXPORT_SYMBOL(drm_self_refresh_helper_cleanup); diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index f12215647801..927e1205d7aa 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -957,4 +957,19 @@ drm_atomic_crtc_needs_modeset(const struct drm_crtc_state *state) state->connectors_changed; } +/** + * drm_atomic_crtc_effectively_active - compute whether crtc is actually active + * @state: &drm_crtc_state for the CRTC + * + * When in self refresh mode, the crtc_state->active value will be false, since + * the crtc is off. However in some cases we're interested in whether the crtc + * is active, or effectively active (ie: it's connected to an active display). + * In these cases, use this function instead of just checking active. + */ +static inline bool +drm_atomic_crtc_effectively_active(const struct drm_crtc_state *state) +{ + return state->active || state->self_refresh_active; +} + #endif /* DRM_ATOMIC_H_ */ diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 071143bc0ebd..c6f8486d8b8f 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -548,6 +548,20 @@ struct drm_connector_state { /** @tv: TV connector state */ struct drm_tv_connector_state tv; + /** + * @self_refresh_aware: + * + * This tracks whether a connector is aware of the self refresh state. + * It should be set to true for those connector implementations which + * understand the self refresh state. This is needed since the crtc + * registers the self refresh helpers and it doesn't know if the + * connectors downstream have implemented self refresh entry/exit. + * + * Drivers should set this to true in atomic_check if they know how to + * handle self_refresh requests. + */ + bool self_refresh_aware; + /** * @picture_aspect_ratio: Connector property to control the * HDMI infoframe aspect ratio setting. diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index dc42b9e35333..128d8b210621 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -54,6 +54,7 @@ struct drm_mode_set; struct drm_file; struct drm_clip_rect; struct drm_printer; +struct drm_self_refresh_data; struct device_node; struct dma_fence; struct edid; @@ -300,6 +301,17 @@ struct drm_crtc_state { */ bool vrr_enabled; + /** + * @self_refresh_active: + * + * Used by the self refresh helpers to denote when a self refresh + * transition is occurring. This will be set on enable/disable callbacks + * when self refresh is being enabled or disabled. In some cases, it may + * not be desirable to fully shut off the crtc during self refresh. + * CRTC's can inspect this flag and determine the best course of action. + */ + bool self_refresh_active; + /** * @event: * @@ -1088,6 +1100,13 @@ struct drm_crtc { * The name of the CRTC's fence timeline. */ char timeline_name[32]; + + /** + * @self_refresh_data: Holds the state for the self refresh helpers + * + * Initialized via drm_self_refresh_helper_register(). + */ + struct drm_self_refresh_data *self_refresh_data; }; /** diff --git a/include/drm/drm_self_refresh_helper.h b/include/drm/drm_self_refresh_helper.h new file mode 100644 index 000000000000..397a583ccca7 --- /dev/null +++ b/include/drm/drm_self_refresh_helper.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2019 Google, Inc. + * + * Authors: + * Sean Paul + */ +#ifndef DRM_SELF_REFRESH_HELPER_H_ +#define DRM_SELF_REFRESH_HELPER_H_ + +struct drm_atomic_state; +struct drm_crtc; + +void drm_self_refresh_helper_alter_state(struct drm_atomic_state *state); + +int drm_self_refresh_helper_init(struct drm_crtc *crtc, + unsigned int entry_delay_ms); + +void drm_self_refresh_helper_cleanup(struct drm_crtc *crtc); +#endif -- cgit v1.2.3