diff options
Diffstat (limited to 'drivers/gpu/drm/stm')
-rw-r--r-- | drivers/gpu/drm/stm/drv.c | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/ltdc.c | 66 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/ltdc.h | 1 |
3 files changed, 63 insertions, 14 deletions
diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index 8698e08313e1..f2021b23554d 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -148,16 +148,16 @@ static int stm_drm_platform_probe(struct platform_device *pdev) ret = drv_load(ddev); if (ret) - goto err_unref; + goto err_put; ret = drm_dev_register(ddev, 0); if (ret) - goto err_unref; + goto err_put; return 0; -err_unref: - drm_dev_unref(ddev); +err_put: + drm_dev_put(ddev); return ret; } @@ -170,7 +170,7 @@ static int stm_drm_platform_remove(struct platform_device *pdev) drm_dev_unregister(ddev); drv_unload(ddev); - drm_dev_unref(ddev); + drm_dev_put(ddev); return 0; } diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index e3121d9e4230..808d9fb627e9 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -445,6 +445,47 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc, reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR); } +#define CLK_TOLERANCE_HZ 50 + +static enum drm_mode_status +ltdc_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct ltdc_device *ldev = crtc_to_ltdc(crtc); + int target = mode->clock * 1000; + int target_min = target - CLK_TOLERANCE_HZ; + int target_max = target + CLK_TOLERANCE_HZ; + int result; + + result = clk_round_rate(ldev->pixel_clk, target); + + DRM_DEBUG_DRIVER("clk rate target %d, available %d\n", target, result); + + /* Filter modes according to the max frequency supported by the pads */ + if (result > ldev->caps.pad_max_freq_hz) + return MODE_CLOCK_HIGH; + + /* + * Accept all "preferred" modes: + * - this is important for panels because panel clock tolerances are + * bigger than hdmi ones and there is no reason to not accept them + * (the fps may vary a little but it is not a problem). + * - the hdmi preferred mode will be accepted too, but userland will + * be able to use others hdmi "valid" modes if necessary. + */ + if (mode->type & DRM_MODE_TYPE_PREFERRED) + return MODE_OK; + + /* + * Filter modes according to the clock value, particularly useful for + * hdmi modes that require precise pixel clocks. + */ + if (result < target_min || result > target_max) + return MODE_CLOCK_RANGE; + + return MODE_OK; +} + static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -559,6 +600,7 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc, } static const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = { + .mode_valid = ltdc_crtc_mode_valid, .mode_fixup = ltdc_crtc_mode_fixup, .mode_set_nofb = ltdc_crtc_mode_set_nofb, .atomic_flush = ltdc_crtc_atomic_flush, @@ -822,13 +864,13 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev, plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL); if (!plane) - return 0; + return NULL; ret = drm_universal_plane_init(ddev, plane, possible_crtcs, <dc_plane_funcs, formats, nb_fmt, NULL, type, NULL); if (ret < 0) - return 0; + return NULL; drm_plane_helper_add(plane, <dc_plane_helper_funcs); @@ -953,11 +995,15 @@ static int ltdc_get_caps(struct drm_device *ddev) * does not work on 2nd layer. */ ldev->caps.non_alpha_only_l1 = true; + ldev->caps.pad_max_freq_hz = 90000000; + if (ldev->caps.hw_version == HWVER_10200) + ldev->caps.pad_max_freq_hz = 65000000; break; case HWVER_20101: ldev->caps.reg_ofs = REG_OFS_4; ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1; ldev->caps.non_alpha_only_l1 = false; + ldev->caps.pad_max_freq_hz = 150000000; break; default: return -ENODEV; @@ -987,14 +1033,13 @@ int ltdc_load(struct drm_device *ddev) &bridge[i]); /* - * If at least one endpoint is ready, continue probing, - * else if at least one endpoint is -EPROBE_DEFER and - * there is no previous ready endpoints, defer probing. + * If at least one endpoint is -EPROBE_DEFER, defer probing, + * else if at least one endpoint is ready, continue probing. */ - if (!ret) + if (ret == -EPROBE_DEFER) + return ret; + else if (!ret) endpoint_not_ready = 0; - else if (ret == -EPROBE_DEFER && endpoint_not_ready) - endpoint_not_ready = -EPROBE_DEFER; } if (endpoint_not_ready) @@ -1037,8 +1082,11 @@ int ltdc_load(struct drm_device *ddev) } } - if (!IS_ERR(rstc)) + if (!IS_ERR(rstc)) { + reset_control_assert(rstc); + usleep_range(10, 20); reset_control_deassert(rstc); + } /* Disable interrupts */ reg_clear(ldev->regs, LTDC_IER, diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h index 1e16d6afb0d2..d5afb8960867 100644 --- a/drivers/gpu/drm/stm/ltdc.h +++ b/drivers/gpu/drm/stm/ltdc.h @@ -18,6 +18,7 @@ struct ltdc_caps { u32 bus_width; /* bus width (32 or 64 bits) */ const u32 *pix_fmt_hw; /* supported pixel formats */ bool non_alpha_only_l1; /* non-native no-alpha formats on layer 1 */ + int pad_max_freq_hz; /* max frequency supported by pad */ }; #define LTDC_MAX_LAYER 4 |