summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/arm
diff options
context:
space:
mode:
authorMihail Atanassov <mihail.atanassov@arm.com>2017-02-13 18:09:01 +0300
committerLiviu Dudau <Liviu.Dudau@arm.com>2017-04-24 15:28:09 +0300
commitc2e7f82d336a451ebb904b8bf9a5a558cf16c39b (patch)
tree7a589b812cf505c31464b2e6628b1eb846600aad /drivers/gpu/drm/arm
parent0274e6a0ba9a4994a449fcd3483ef530027e152f (diff)
downloadlinux-c2e7f82d336a451ebb904b8bf9a5a558cf16c39b.tar.xz
drm: mali-dp: Check the mclk rate and allow up/down scaling
When downscaling, mclk needs to be sufficiently higher than pxlclk in order to be able to fetch the higher-resolution data and produce output pixels. When not scaling, or when upscaling, mclk can be equal to pxlclk. Since the driver doesn't control mclk, just ensure that the requirement is satisfied with the current clock rate. Signed-off-by: Mihail Atanassov <mihail.atanassov@arm.com> Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Diffstat (limited to 'drivers/gpu/drm/arm')
-rw-r--r--drivers/gpu/drm/arm/malidp_crtc.c23
-rw-r--r--drivers/gpu/drm/arm/malidp_hw.c70
-rw-r--r--drivers/gpu/drm/arm/malidp_hw.h4
3 files changed, 85 insertions, 12 deletions
diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c
index b0f0365efd23..19f1f3b34691 100644
--- a/drivers/gpu/drm/arm/malidp_crtc.c
+++ b/drivers/gpu/drm/arm/malidp_crtc.c
@@ -36,13 +36,6 @@ static bool malidp_crtc_mode_fixup(struct drm_crtc *crtc,
long rate, req_rate = mode->crtc_clock * 1000;
if (req_rate) {
- rate = clk_round_rate(hwdev->mclk, req_rate);
- if (rate < req_rate) {
- DRM_DEBUG_DRIVER("mclk clock unable to reach %d kHz\n",
- mode->crtc_clock);
- return false;
- }
-
rate = clk_round_rate(hwdev->pxlclk, req_rate);
if (rate != req_rate) {
DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n",
@@ -250,17 +243,21 @@ static int malidp_crtc_atomic_check_ctm(struct drm_crtc *crtc,
static int malidp_crtc_atomic_check_scaling(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
+ struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
+ struct malidp_hw_device *hwdev = malidp->dev;
struct malidp_crtc_state *cs = to_malidp_crtc_state(state);
struct malidp_se_config *s = &cs->scaler_config;
struct drm_plane *plane;
+ struct videomode vm;
const struct drm_plane_state *pstate;
u32 h_upscale_factor = 0; /* U16.16 */
u32 v_upscale_factor = 0; /* U16.16 */
u8 scaling = cs->scaled_planes_mask;
+ int ret;
if (!scaling) {
s->scale_enable = false;
- return 0;
+ goto mclk_calc;
}
/* The scaling engine can only handle one plane at a time. */
@@ -284,10 +281,6 @@ static int malidp_crtc_atomic_check_scaling(struct drm_crtc *crtc,
h_upscale_factor = (u32)(crtc_w / pstate->src_w);
v_upscale_factor = (u32)(crtc_h / pstate->src_h);
- /* Downscaling won't work when mclk == pxlclk. */
- if (!(h_upscale_factor >> 16) || !(v_upscale_factor >> 16))
- return -EINVAL;
-
s->enhancer_enable = ((h_upscale_factor >> 16) >= 2 ||
(v_upscale_factor >> 16) >= 2);
@@ -323,6 +316,12 @@ static int malidp_crtc_atomic_check_scaling(struct drm_crtc *crtc,
s->scale_enable = true;
s->hcoeff = malidp_se_select_coeffs(h_upscale_factor);
s->vcoeff = malidp_se_select_coeffs(v_upscale_factor);
+
+mclk_calc:
+ drm_display_mode_to_videomode(&state->adjusted_mode, &vm);
+ ret = hwdev->se_calc_mclk(hwdev, s, &vm);
+ if (ret < 0)
+ return -EINVAL;
return 0;
}
diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c
index e444c23a8261..28360b8542f7 100644
--- a/drivers/gpu/drm/arm/malidp_hw.c
+++ b/drivers/gpu/drm/arm/malidp_hw.c
@@ -12,6 +12,7 @@
* in an attempt to provide to the rest of the driver code a unified view
*/
+#include <linux/clk.h>
#include <linux/types.h>
#include <linux/io.h>
#include <drm/drmP.h>
@@ -334,6 +335,39 @@ static int malidp500_se_set_scaling_coeffs(struct malidp_hw_device *hwdev,
return 0;
}
+static long malidp500_se_calc_mclk(struct malidp_hw_device *hwdev,
+ struct malidp_se_config *se_config,
+ struct videomode *vm)
+{
+ unsigned long mclk;
+ unsigned long pxlclk = vm->pixelclock; /* Hz */
+ unsigned long htotal = vm->hactive + vm->hfront_porch +
+ vm->hback_porch + vm->hsync_len;
+ unsigned long input_size = se_config->input_w * se_config->input_h;
+ unsigned long a = 10;
+ long ret;
+
+ /*
+ * mclk = max(a, 1.5) * pxlclk
+ *
+ * To avoid float calculaiton, using 15 instead of 1.5 and div by
+ * 10 to get mclk.
+ */
+ if (se_config->scale_enable) {
+ a = 15 * input_size / (htotal * se_config->output_h);
+ if (a < 15)
+ a = 15;
+ }
+ mclk = a * pxlclk / 10;
+ ret = clk_get_rate(hwdev->mclk);
+ if (ret < mclk) {
+ DRM_DEBUG_DRIVER("mclk requirement of %lu kHz can't be met.\n",
+ mclk / 1000);
+ return -EINVAL;
+ }
+ return ret;
+}
+
static int malidp550_query_hw(struct malidp_hw_device *hwdev)
{
u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
@@ -521,6 +555,39 @@ static int malidp550_se_set_scaling_coeffs(struct malidp_hw_device *hwdev,
return 0;
}
+static long malidp550_se_calc_mclk(struct malidp_hw_device *hwdev,
+ struct malidp_se_config *se_config,
+ struct videomode *vm)
+{
+ unsigned long mclk;
+ unsigned long pxlclk = vm->pixelclock;
+ unsigned long htotal = vm->hactive + vm->hfront_porch +
+ vm->hback_porch + vm->hsync_len;
+ unsigned long numerator = 1, denominator = 1;
+ long ret;
+
+ if (se_config->scale_enable) {
+ numerator = max(se_config->input_w, se_config->output_w) *
+ se_config->input_h;
+ numerator += se_config->output_w *
+ (se_config->output_h -
+ min(se_config->input_h, se_config->output_h));
+ denominator = (htotal - 2) * se_config->output_h;
+ }
+
+ /* mclk can't be slower than pxlclk. */
+ if (numerator < denominator)
+ numerator = denominator = 1;
+ mclk = (pxlclk * numerator) / denominator;
+ ret = clk_get_rate(hwdev->mclk);
+ if (ret < mclk) {
+ DRM_DEBUG_DRIVER("mclk requirement of %lu kHz can't be met.\n",
+ mclk / 1000);
+ return -EINVAL;
+ }
+ return ret;
+}
+
static int malidp650_query_hw(struct malidp_hw_device *hwdev)
{
u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
@@ -586,6 +653,7 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
.modeset = malidp500_modeset,
.rotmem_required = malidp500_rotmem_required,
.se_set_scaling_coeffs = malidp500_se_set_scaling_coeffs,
+ .se_calc_mclk = malidp500_se_calc_mclk,
.features = MALIDP_DEVICE_LV_HAS_3_STRIDES,
},
[MALIDP_550] = {
@@ -622,6 +690,7 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
.modeset = malidp550_modeset,
.rotmem_required = malidp550_rotmem_required,
.se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs,
+ .se_calc_mclk = malidp550_se_calc_mclk,
.features = 0,
},
[MALIDP_650] = {
@@ -659,6 +728,7 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
.modeset = malidp550_modeset,
.rotmem_required = malidp550_rotmem_required,
.se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs,
+ .se_calc_mclk = malidp550_se_calc_mclk,
.features = 0,
},
};
diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h
index a93ae0a951a3..849ad9a30c3a 100644
--- a/drivers/gpu/drm/arm/malidp_hw.h
+++ b/drivers/gpu/drm/arm/malidp_hw.h
@@ -177,6 +177,10 @@ struct malidp_hw_device {
struct malidp_se_config *se_config,
struct malidp_se_config *old_config);
+ long (*se_calc_mclk)(struct malidp_hw_device *hwdev,
+ struct malidp_se_config *se_config,
+ struct videomode *vm);
+
u8 features;
u8 min_line_size;