diff options
author | Dave Airlie <airlied@redhat.com> | 2014-10-20 16:31:53 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2014-12-09 09:56:49 +1000 |
commit | b0ee9e7fa5b461a91f24d1d03b10c6bf162c86f9 (patch) | |
tree | dd12f04f8e18574f134a66bd8ceb93fbf5109606 /drivers/gpu/drm/drm_fb_helper.c | |
parent | 6f134d7bb4347ab4c66ef123efb838fedb54186f (diff) |
drm/fb: add support for tiled monitor configurations. (v2)
This adds fbdev/con support for tiled monitors, so that we
only set a mode on the correct half of the monitor, or
span the two halves if needed.
v2: remove unneeded ERROR, fix | vs ||
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_fb_helper.c | 121 |
1 files changed, 101 insertions, 20 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index a467460f2aa6..52ce26d6b4fb 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1000,19 +1000,21 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, crtc_count = 0; for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_display_mode *desired_mode; + int x, y; desired_mode = fb_helper->crtc_info[i].desired_mode; - + x = fb_helper->crtc_info[i].x; + y = fb_helper->crtc_info[i].y; if (desired_mode) { if (gamma_size == 0) gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; - if (desired_mode->hdisplay < sizes.fb_width) - sizes.fb_width = desired_mode->hdisplay; - if (desired_mode->vdisplay < sizes.fb_height) - sizes.fb_height = desired_mode->vdisplay; - if (desired_mode->hdisplay > sizes.surface_width) - sizes.surface_width = desired_mode->hdisplay; - if (desired_mode->vdisplay > sizes.surface_height) - sizes.surface_height = desired_mode->vdisplay; + if (desired_mode->hdisplay + x < sizes.fb_width) + sizes.fb_width = desired_mode->hdisplay + x; + if (desired_mode->vdisplay + y < sizes.fb_height) + sizes.fb_height = desired_mode->vdisplay + y; + if (desired_mode->hdisplay + x > sizes.surface_width) + sizes.surface_width = desired_mode->hdisplay + x; + if (desired_mode->vdisplay + y > sizes.surface_height) + sizes.surface_height = desired_mode->vdisplay + y; crtc_count++; } } @@ -1312,6 +1314,7 @@ static void drm_enable_connectors(struct drm_fb_helper *fb_helper, 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) { int count, i, j; @@ -1383,27 +1386,88 @@ 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) +{ + struct drm_fb_helper_connector *fb_helper_conn; + int i; + int hoffset = 0, voffset = 0; + + for (i = 0; i < fb_helper->connector_count; i++) { + fb_helper_conn = fb_helper->connector_info[i]; + if (!fb_helper_conn->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); + continue; + } + if (fb_helper_conn->connector->tile_h_loc < h_idx) + hoffset += modes[i]->hdisplay; + + if (fb_helper_conn->connector->tile_v_loc < v_idx) + voffset += modes[i]->vdisplay; + } + offsets[idx].x = hoffset; + offsets[idx].y = voffset; + DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx); + 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) { struct drm_fb_helper_connector *fb_helper_conn; int i; - + uint64_t conn_configured = 0, mask; + int tile_pass = 0; + mask = (1 << fb_helper->connector_count) - 1; +retry: for (i = 0; i < fb_helper->connector_count; i++) { fb_helper_conn = fb_helper->connector_info[i]; - if (enabled[i] == false) + if (conn_configured & (1 << i)) + continue; + + if (enabled[i] == false) { + conn_configured |= (1 << i); + continue; + } + + /* first pass over all the untiled connectors */ + if (tile_pass == 0 && fb_helper_conn->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) + continue; + + } else { + if (fb_helper_conn->connector->tile_h_loc != tile_pass -1 && + fb_helper_conn->connector->tile_v_loc != tile_pass - 1) + /* if this tile_pass doesn't cover any of the tiles - keep going */ + continue; + + /* 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_DEBUG_KMS("looking for cmdline mode on connector %d\n", fb_helper_conn->connector->base.id); /* got for command line mode first */ modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); if (!modes[i]) { - DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", - fb_helper_conn->connector->base.id); + 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); } /* No preferred modes, pick one off the list */ @@ -1413,6 +1477,12 @@ static bool drm_target_preferred(struct drm_fb_helper *fb_helper, } DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : "none"); + conn_configured |= (1 << i); + } + + if ((conn_configured & mask) != mask) { + tile_pass++; + goto retry; } return true; } @@ -1502,6 +1572,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; struct drm_fb_helper_crtc **crtcs; struct drm_display_mode **modes; + struct drm_fb_offset *offsets; struct drm_mode_set *modeset; bool *enabled; int width, height; @@ -1516,9 +1587,11 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); modes = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_display_mode *), GFP_KERNEL); + offsets = kcalloc(dev->mode_config.num_connector, + sizeof(struct drm_fb_offset), GFP_KERNEL); enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool), GFP_KERNEL); - if (!crtcs || !modes || !enabled) { + if (!crtcs || !modes || !enabled || !offsets) { DRM_ERROR("Memory allocation failed\n"); goto out; } @@ -1528,14 +1601,16 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) if (!(fb_helper->funcs->initial_config && fb_helper->funcs->initial_config(fb_helper, crtcs, modes, + offsets, enabled, width, height))) { memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0])); memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0])); + memset(offsets, 0, dev->mode_config.num_connector*sizeof(offsets[0])); - if (!drm_target_cloned(fb_helper, - modes, enabled, width, height) && - !drm_target_preferred(fb_helper, - modes, enabled, width, height)) + if (!drm_target_cloned(fb_helper, modes, offsets, + enabled, width, height) && + !drm_target_preferred(fb_helper, modes, offsets, + enabled, width, height)) DRM_ERROR("Unable to find initial modes\n"); DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", @@ -1555,18 +1630,23 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) for (i = 0; i < fb_helper->connector_count; i++) { struct drm_display_mode *mode = modes[i]; struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; + struct drm_fb_offset *offset = &offsets[i]; modeset = &fb_crtc->mode_set; if (mode && fb_crtc) { - DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", - mode->name, fb_crtc->mode_set.crtc->base.id); + DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", + mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y); fb_crtc->desired_mode = mode; + fb_crtc->x = offset->x; + fb_crtc->y = offset->y; if (modeset->mode) drm_mode_destroy(dev, modeset->mode); modeset->mode = drm_mode_duplicate(dev, fb_crtc->desired_mode); modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; modeset->fb = fb_helper->fb; + modeset->x = offset->x; + modeset->y = offset->y; } } @@ -1583,6 +1663,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) out: kfree(crtcs); kfree(modes); + kfree(offsets); kfree(enabled); } |