Hi Boris, Am Dienstag, den 26.05.2015, 11:28 +0200 schrieb Boris Brezillon: > Hi David, > > On Thu, 21 May 2015 11:06:56 +0200 > David Dueck <davidcdueck@xxxxxxxxxxxxxx> wrote: > > > drm_panel supports querying timing ranges. If the supplied mode does > > not work with the hlcdc we query the panel and try to find a suitable > > mode. > > This patch looks good to me. > > Philip, Thierry, could you confirm this is the correct way of dealing > with timing ranges. I wonder about two things: This implementation minimizes the sum of absolute differences between chosen and typical values. I wonder if it would be better to try and minimize the difference between the chosen and nominal vertical refresh rate. Is this something that should be done earlier, in create_panel_output, so that the connector's modes already contain the corrected settings? regards Philipp > > > > Signed-off-by: David Dueck <davidcdueck@xxxxxxxxxxxxxx> > > --- > > drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 118 +++++++++++++++++++---- > > 1 file changed, 98 insertions(+), 20 deletions(-) > > > > diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c > > index 9c45130..ea36c24 100644 > > --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c > > +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c > > @@ -20,6 +20,7 @@ > > */ > > > > #include <linux/of_graph.h> > > +#include <video/display_timing.h> > > > > #include <drm/drmP.h> > > #include <drm/drm_panel.h> > > @@ -78,6 +79,8 @@ drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder) > > struct atmel_hlcdc_panel { > > struct atmel_hlcdc_rgb_output base; > > struct drm_panel *panel; > > + struct display_timing *timings; > > + unsigned int num_timings; > > }; > > > > static inline struct atmel_hlcdc_panel * > > @@ -104,14 +107,6 @@ static void atmel_hlcdc_panel_encoder_disable(struct drm_encoder *encoder) > > drm_panel_disable(panel->panel); > > } > > > > -static bool > > -atmel_hlcdc_panel_encoder_mode_fixup(struct drm_encoder *encoder, > > - const struct drm_display_mode *mode, > > - struct drm_display_mode *adjusted) > > -{ > > - return true; > > -} > > - > > static void > > atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder, > > struct drm_display_mode *mode, > > @@ -142,8 +137,86 @@ atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder, > > cfg); > > } > > > > +/** > > + * atmel_hlcdc_choose_parameter - choose timing parameter > > + * @dc_min: minimum parameter value allowed by dc > > + * @dc_max: maximum parameter value allowed by dc > > + * @te: parameter range allowed by panel > > + * @result: chosen parameter > > + * > > + * DESCRIPTION: > > + * If there is overlap in the allowed ranges, we will pick a parameter > > + * minimizing the distance to te.typ. If not, we return te.min or te.max. > > + **/ > > +static u32 atmel_hlcdc_choose_parameter(u32 dc_min, u32 dc_max, > > + struct timing_entry te) > > +{ > > + if (te.typ <= dc_max && te.typ >= dc_min) > > + return te.typ; > > + else if (te.typ > dc_max) > > + return max(dc_max, te.min); > > + else > > + return min(dc_min, te.max); > > +} > > + > > +static void atmel_hlcdc_calculate_mode(struct display_timing *timings, > > + struct drm_display_mode *adjusted_mode) > > +{ > > + u32 hsync_len, hfront_porch, hback_porch; > > + u32 vsync_len, vfront_porch, vback_porch; > > + > > + hsync_len = atmel_hlcdc_choose_parameter(1, 0x40, timings->hsync_len); > > + vsync_len = atmel_hlcdc_choose_parameter(1, 0x40, timings->vsync_len); > > + > > + hfront_porch = atmel_hlcdc_choose_parameter(1, 0x200, > > + timings->hfront_porch); > > + hback_porch = atmel_hlcdc_choose_parameter(1, 0x200, > > + timings->hback_porch); > > + vfront_porch = atmel_hlcdc_choose_parameter(1, 0x40, > > + timings->vfront_porch); > > + vback_porch = atmel_hlcdc_choose_parameter(0, 0x40, > > + timings->vback_porch); > > + > > + adjusted_mode->hsync_start = adjusted_mode->hdisplay + hfront_porch; > > + adjusted_mode->hsync_end = adjusted_mode->hsync_start + hsync_len; > > + adjusted_mode->htotal = adjusted_mode->hsync_end + hback_porch; > > + > > + adjusted_mode->vsync_start = adjusted_mode->vdisplay + vfront_porch; > > + adjusted_mode->vsync_end = adjusted_mode->vsync_start + vsync_len; > > + adjusted_mode->vtotal = adjusted_mode->vsync_end + vback_porch; > > +} > > + > > +static int > > +atmel_hlcdc_panel_encoder_atomic_check(struct drm_encoder *encoder, > > + struct drm_crtc_state *crtc_state, > > + struct drm_connector_state *conn_state) > > +{ > > + struct atmel_hlcdc_rgb_output *rgb = > > + drm_encoder_to_atmel_hlcdc_rgb_output(encoder); > > + struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb); > > + struct drm_display_mode *mode = &crtc_state->mode; > > + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; > > + int i; > > + > > + if (atmel_hlcdc_dc_mode_valid(rgb->dc, mode) == MODE_OK) > > + return 0; > > + > > + for (i = 0; i < panel->num_timings; i++) { > > + if (panel->timings->hactive.typ != mode->hdisplay || > > + panel->timings->vactive.typ != mode->vdisplay) > > + continue; > > + > > + atmel_hlcdc_calculate_mode(panel->timings, adjusted_mode); > > + if (atmel_hlcdc_dc_mode_valid(rgb->dc, adjusted_mode) == > > + MODE_OK) > > + return 0; > > + } > > + > > + return -EINVAL; > > +} > > + > > static struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = { > > - .mode_fixup = atmel_hlcdc_panel_encoder_mode_fixup, > > + .atomic_check = atmel_hlcdc_panel_encoder_atomic_check, > > .mode_set = atmel_hlcdc_rgb_encoder_mode_set, > > .disable = atmel_hlcdc_panel_encoder_disable, > > .enable = atmel_hlcdc_panel_encoder_enable, > > @@ -168,16 +241,6 @@ static int atmel_hlcdc_panel_get_modes(struct drm_connector *connector) > > return panel->panel->funcs->get_modes(panel->panel); > > } > > > > -static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector, > > - struct drm_display_mode *mode) > > -{ > > - struct atmel_hlcdc_rgb_output *rgb = > > - drm_connector_to_atmel_hlcdc_rgb_output(connector); > > - > > - return atmel_hlcdc_dc_mode_valid(rgb->dc, mode); > > -} > > - > > - > > > > static struct drm_encoder * > > atmel_hlcdc_rgb_best_encoder(struct drm_connector *connector) > > @@ -190,7 +253,6 @@ atmel_hlcdc_rgb_best_encoder(struct drm_connector *connector) > > > > static struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helper_funcs = { > > .get_modes = atmel_hlcdc_panel_get_modes, > > - .mode_valid = atmel_hlcdc_rgb_mode_valid, > > .best_encoder = atmel_hlcdc_rgb_best_encoder, > > }; > > > > @@ -273,6 +335,22 @@ static int atmel_hlcdc_create_panel_output(struct drm_device *dev, > > drm_panel_attach(p, &panel->base.connector); > > panel->panel = p; > > > > + if (!panel->panel->funcs->get_timings) > > + return 0; > > + > > + panel->num_timings = > > + panel->panel->funcs->get_timings(panel->panel, 0, NULL); > > + panel->timings = > > + devm_kzalloc(dev->dev, > > + panel->num_timings * sizeof(struct display_timing), > > + GFP_KERNEL); > > + > > + if (!panel->timings) > > + return 0; > > + > > + panel->panel->funcs->get_timings(panel->panel, panel->num_timings, > > + panel->timings); > > + > > return 0; > > > > err_encoder_cleanup: > > > _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel