Hello,
This series is a collection of fixes and improvements for the atmel-hlcdc driver.
The main feature added here is the support for external RGB -> XXX bridges (patch 6 and 7).
The first patch is a fix preventing a potential memory leak. Patch 2 is adding support for asynchronous mode setting, which was supported before the migration to atomic mode setting.
Patch 3 is just a minor fix to expose the real encoder and connector types (we are currently exposing an LVDS encoder/connector, which is wrong since the display controller output the pixel stream in raw RGB).
Patch 4 is removing useless fields and functions which were left when moving to atomic modesetting.
And patch 8 is just a cosmetic patch moving the mode checking code from ->atomic_check() to ->mode_fixup().
Best Regards,
Boris
Boris Brezillon (8): drm: atmel-hlcdc: add a ->cleanup_fb() operation drm: atmel-hlcdc: support asynchronous atomic commit operations drm: atmel-hlcdc: fix connector and encoder types drm: atmel-hlcdc: remove leftovers from atomic mode setting migration drm: atmel-hlcdc: support extended timing ranges on sama5d4 and sama5d2 drm: atmel-hlcdc: move output mode selection in CRTC implementation drm: atmel-hlcdc: rework the output code to support drm bridges drm: atmel-hlcdc: check display mode validity in crtc->mode_fixup()
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 148 +++++++++++++- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 123 ++++++++++- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 16 ++ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 249 ++++++++++------------- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 22 +- 5 files changed, 400 insertions(+), 158 deletions(-)
Add a ->cleanup_fb() operation to avoid memory leaks when the atomic operation is interrupted after the ->prepare_fb() call.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com Fixes 2389fc1 ("drm: atmel-hlcdc: Atomic mode-setting conversion") --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 2 ++ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index cf6b375..e88e349 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -81,11 +81,13 @@ struct atmel_hlcdc_plane_properties { * @layer: HLCDC layer structure * @properties: pointer to the property definitions structure * @rotation: current rotation status + * @prepared: flagging the plane has prepared for an atomic update */ struct atmel_hlcdc_plane { struct drm_plane base; struct atmel_hlcdc_layer layer; struct atmel_hlcdc_plane_properties *properties; + bool prepared; };
static inline struct atmel_hlcdc_plane * diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index 1ffe9c3..35027d0 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -715,11 +715,25 @@ static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p, const struct drm_plane_state *new_state) { struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); + int ret;
- if (!new_state->fb) - return 0; + ret = atmel_hlcdc_layer_update_start(&plane->layer); + if (!ret) + plane->prepared = true; + + return ret; +} + +static void atmel_hlcdc_plane_cleanup_fb(struct drm_plane *p, + const struct drm_plane_state *old_state) +{ + struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); + + if (!plane->prepared) + return;
- return atmel_hlcdc_layer_update_start(&plane->layer); + atmel_hlcdc_layer_update_rollback(&plane->layer); + plane->prepared = false; }
static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, @@ -739,6 +753,7 @@ static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, atmel_hlcdc_plane_update_disc_area(plane, state);
atmel_hlcdc_layer_update_commit(&plane->layer); + plane->prepared = false; }
static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, @@ -844,6 +859,7 @@ static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = { .prepare_fb = atmel_hlcdc_plane_prepare_fb, + .cleanup_fb = atmel_hlcdc_plane_cleanup_fb, .atomic_check = atmel_hlcdc_plane_atomic_check, .atomic_update = atmel_hlcdc_plane_atomic_update, .atomic_disable = atmel_hlcdc_plane_atomic_disable,
Le 06/01/2016 11:19, Boris Brezillon a écrit :
Add a ->cleanup_fb() operation to avoid memory leaks when the atomic operation is interrupted after the ->prepare_fb() call.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com Fixes 2389fc1 ("drm: atmel-hlcdc: Atomic mode-setting conversion")
Seems okay: Reviewed-by: Nicolas Ferre nicolas.ferre@atmel.com
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 2 ++ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index cf6b375..e88e349 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -81,11 +81,13 @@ struct atmel_hlcdc_plane_properties {
- @layer: HLCDC layer structure
- @properties: pointer to the property definitions structure
- @rotation: current rotation status
*/
- @prepared: flagging the plane has prepared for an atomic update
struct atmel_hlcdc_plane { struct drm_plane base; struct atmel_hlcdc_layer layer; struct atmel_hlcdc_plane_properties *properties;
- bool prepared;
};
static inline struct atmel_hlcdc_plane * diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index 1ffe9c3..35027d0 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -715,11 +715,25 @@ static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p, const struct drm_plane_state *new_state) { struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
- int ret;
- if (!new_state->fb)
return 0;
- ret = atmel_hlcdc_layer_update_start(&plane->layer);
- if (!ret)
plane->prepared = true;
- return ret;
+}
+static void atmel_hlcdc_plane_cleanup_fb(struct drm_plane *p,
const struct drm_plane_state *old_state)
+{
- struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
- if (!plane->prepared)
return;
- return atmel_hlcdc_layer_update_start(&plane->layer);
- atmel_hlcdc_layer_update_rollback(&plane->layer);
- plane->prepared = false;
}
static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, @@ -739,6 +753,7 @@ static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, atmel_hlcdc_plane_update_disc_area(plane, state);
atmel_hlcdc_layer_update_commit(&plane->layer);
- plane->prepared = false;
}
static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, @@ -844,6 +859,7 @@ static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = { .prepare_fb = atmel_hlcdc_plane_prepare_fb,
- .cleanup_fb = atmel_hlcdc_plane_cleanup_fb, .atomic_check = atmel_hlcdc_plane_atomic_check, .atomic_update = atmel_hlcdc_plane_atomic_update, .atomic_disable = atmel_hlcdc_plane_atomic_disable,
drm_atomic_helper_commit() does not support asynchronous commits. Replace it by a specific commit function supporting these kind of requests.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 94 +++++++++++++++++++++++++++- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 5 ++ 2 files changed, 98 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index a45b32b..d760475 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -427,11 +427,102 @@ static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev) } }
+struct atmel_hlcdc_dc_commit { + struct work_struct work; + struct drm_device *dev; + struct drm_atomic_state *state; +}; + +static void +atmel_hlcdc_dc_atomic_complete(struct atmel_hlcdc_dc_commit *commit) +{ + struct drm_device *dev = commit->dev; + struct atmel_hlcdc_dc *dc = dev->dev_private; + struct drm_atomic_state *old_state = commit->state; + + /* Apply the atomic update. */ + drm_atomic_helper_commit_modeset_disables(dev, old_state); + drm_atomic_helper_commit_planes(dev, old_state, false); + drm_atomic_helper_commit_modeset_enables(dev, old_state); + + drm_atomic_helper_wait_for_vblanks(dev, old_state); + + drm_atomic_helper_cleanup_planes(dev, old_state); + + drm_atomic_state_free(old_state); + + /* Complete the commit, wake up any waiter. */ + spin_lock(&dc->commit.wait.lock); + dc->commit.pending = false; + wake_up_all_locked(&dc->commit.wait); + spin_unlock(&dc->commit.wait.lock); + + kfree(commit); +} + +static void atmel_hlcdc_dc_atomic_work(struct work_struct *work) +{ + struct atmel_hlcdc_dc_commit *commit = + container_of(work, struct atmel_hlcdc_dc_commit, work); + + atmel_hlcdc_dc_atomic_complete(commit); +} + +static int atmel_hlcdc_dc_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool async) +{ + struct atmel_hlcdc_dc *dc = dev->dev_private; + struct atmel_hlcdc_dc_commit *commit; + int ret; + + ret = drm_atomic_helper_prepare_planes(dev, state); + if (ret) + return ret; + + /* Allocate the commit object. */ + commit = kzalloc(sizeof(*commit), GFP_KERNEL); + if (!commit) { + ret = -ENOMEM; + goto error; + } + + INIT_WORK(&commit->work, atmel_hlcdc_dc_atomic_work); + commit->dev = dev; + commit->state = state; + + spin_lock(&dc->commit.wait.lock); + ret = wait_event_interruptible_locked(dc->commit.wait, + !dc->commit.pending); + if (ret == 0) + dc->commit.pending = true; + spin_unlock(&dc->commit.wait.lock); + + if (ret) { + kfree(commit); + goto error; + } + + /* Swap the state, this is the point of no return. */ + drm_atomic_helper_swap_state(dev, state); + + if (async) + queue_work(dc->wq, &commit->work); + else + atmel_hlcdc_dc_atomic_complete(commit); + + return 0; + +error: + drm_atomic_helper_cleanup_planes(dev, state); + return ret; +} + static const struct drm_mode_config_funcs mode_config_funcs = { .fb_create = atmel_hlcdc_fb_create, .output_poll_changed = atmel_hlcdc_fb_output_poll_changed, .atomic_check = drm_atomic_helper_check, - .atomic_commit = drm_atomic_helper_commit, + .atomic_commit = atmel_hlcdc_dc_atomic_commit, };
static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) @@ -509,6 +600,7 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) if (!dc->wq) return -ENOMEM;
+ init_waitqueue_head(&dc->commit.wait); dc->desc = match->data; dc->hlcdc = dev_get_drvdata(dev->dev->parent); dev->dev_private = dc; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index e88e349..6a762c9 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -130,6 +130,7 @@ struct atmel_hlcdc_planes { * @planes: instantiated planes * @layers: active HLCDC layer * @wq: display controller workqueue + * @commit: used for async commit handling */ struct atmel_hlcdc_dc { const struct atmel_hlcdc_dc_desc *desc; @@ -139,6 +140,10 @@ struct atmel_hlcdc_dc { struct atmel_hlcdc_planes *planes; struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS]; struct workqueue_struct *wq; + struct { + wait_queue_head_t wait; + bool pending; + } commit; };
extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats;
The hlcdc IP keep the pixel stream in raw RGB mode, and does not provide any specific connector. Since DRM_MODE_CONNECTOR_RAW_RGB does not exist, use DRM_MODE_CONNECTOR_Unknown.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 0f7ec01..49494e9 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -256,7 +256,7 @@ static int atmel_hlcdc_create_panel_output(struct drm_device *dev, &atmel_hlcdc_panel_encoder_helper_funcs); ret = drm_encoder_init(dev, &panel->base.encoder, &atmel_hlcdc_panel_encoder_funcs, - DRM_MODE_ENCODER_LVDS, NULL); + DRM_MODE_ENCODER_NONE, NULL); if (ret) return ret;
@@ -266,7 +266,7 @@ static int atmel_hlcdc_create_panel_output(struct drm_device *dev, &atmel_hlcdc_panel_connector_helper_funcs); ret = drm_connector_init(dev, &panel->base.connector, &atmel_hlcdc_panel_connector_funcs, - DRM_MODE_CONNECTOR_LVDS); + DRM_MODE_CONNECTOR_Unknown); if (ret) goto err_encoder_cleanup;
Le 06/01/2016 11:19, Boris Brezillon a écrit :
The hlcdc IP keep the pixel stream in raw RGB mode, and does not provide any specific connector. Since DRM_MODE_CONNECTOR_RAW_RGB does not exist, use DRM_MODE_CONNECTOR_Unknown.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com
Yes: Acked-by: Nicolas Ferre nicolas.ferre@atmel.com
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 0f7ec01..49494e9 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -256,7 +256,7 @@ static int atmel_hlcdc_create_panel_output(struct drm_device *dev, &atmel_hlcdc_panel_encoder_helper_funcs); ret = drm_encoder_init(dev, &panel->base.encoder, &atmel_hlcdc_panel_encoder_funcs,
DRM_MODE_ENCODER_LVDS, NULL);
if (ret) return ret;DRM_MODE_ENCODER_NONE, NULL);
@@ -266,7 +266,7 @@ static int atmel_hlcdc_create_panel_output(struct drm_device *dev, &atmel_hlcdc_panel_connector_helper_funcs); ret = drm_connector_init(dev, &panel->base.connector, &atmel_hlcdc_panel_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
if (ret) goto err_encoder_cleanup;DRM_MODE_CONNECTOR_Unknown);
The ->dpms field is no longer used and can be removed. The same goes for the dummy ->mode_fixup() implementation which always returns true.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 12 ------------ 1 file changed, 12 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 49494e9..2255dc9 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -44,13 +44,11 @@ enum atmel_hlcdc_connector_rgb_mode { * @connector: DRM connector * @encoder: DRM encoder * @dc: pointer to the atmel_hlcdc_dc structure - * @dpms: current DPMS mode */ struct atmel_hlcdc_rgb_output { struct drm_connector connector; struct drm_encoder encoder; struct atmel_hlcdc_dc *dc; - int dpms; };
static inline struct atmel_hlcdc_rgb_output * @@ -104,14 +102,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, @@ -147,7 +137,6 @@ atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder, }
static const struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = { - .mode_fixup = atmel_hlcdc_panel_encoder_mode_fixup, .mode_set = atmel_hlcdc_rgb_encoder_mode_set, .disable = atmel_hlcdc_panel_encoder_disable, .enable = atmel_hlcdc_panel_encoder_enable, @@ -248,7 +237,6 @@ static int atmel_hlcdc_create_panel_output(struct drm_device *dev, if (!panel) return -EINVAL;
- panel->base.dpms = DRM_MODE_DPMS_OFF;
panel->base.dc = dc;
Le 06/01/2016 11:19, Boris Brezillon a écrit :
The ->dpms field is no longer used and can be removed. The same goes for the dummy ->mode_fixup() implementation which always returns true.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com
Acked-by: Nicolas Ferre nicolas.ferre@atmel.com
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 12 ------------ 1 file changed, 12 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 49494e9..2255dc9 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -44,13 +44,11 @@ enum atmel_hlcdc_connector_rgb_mode {
- @connector: DRM connector
- @encoder: DRM encoder
- @dc: pointer to the atmel_hlcdc_dc structure
*/
- @dpms: current DPMS mode
struct atmel_hlcdc_rgb_output { struct drm_connector connector; struct drm_encoder encoder; struct atmel_hlcdc_dc *dc;
- int dpms;
};
static inline struct atmel_hlcdc_rgb_output * @@ -104,14 +102,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, @@ -147,7 +137,6 @@ atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder, }
static const struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = {
- .mode_fixup = atmel_hlcdc_panel_encoder_mode_fixup, .mode_set = atmel_hlcdc_rgb_encoder_mode_set, .disable = atmel_hlcdc_panel_encoder_disable, .enable = atmel_hlcdc_panel_encoder_enable,
@@ -248,7 +237,6 @@ static int atmel_hlcdc_create_panel_output(struct drm_device *dev, if (!panel) return -EINVAL;
panel->base.dpms = DRM_MODE_DPMS_OFF;
panel->base.dc = dc;
The display timings on old SoCs older than the sama5d4 are quite limited and prevent the use of many displays. Add support for extended timing ranges on sama5d2 and sama5d4.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 24 ++++++++++++++++++------ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 6 ++++++ 2 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index d760475..66f97e1 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -50,6 +50,9 @@ static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9n12 = { .min_height = 0, .max_width = 1280, .max_height = 860, + .max_spw = 0x3f, + .max_vpw = 0x3f, + .max_hpw = 0xff, .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9n12_layers), .layers = atmel_hlcdc_at91sam9n12_layers, }; @@ -134,6 +137,9 @@ static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9x5 = { .min_height = 0, .max_width = 800, .max_height = 600, + .max_spw = 0x3f, + .max_vpw = 0x3f, + .max_hpw = 0xff, .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9x5_layers), .layers = atmel_hlcdc_at91sam9x5_layers, }; @@ -237,6 +243,9 @@ static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = { .min_height = 0, .max_width = 2048, .max_height = 2048, + .max_spw = 0x3f, + .max_vpw = 0x3f, + .max_hpw = 0x1ff, .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers), .layers = atmel_hlcdc_sama5d3_layers, }; @@ -320,6 +329,9 @@ static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d4 = { .min_height = 0, .max_width = 2048, .max_height = 2048, + .max_spw = 0xff, + .max_vpw = 0xff, + .max_hpw = 0x3ff, .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d4_layers), .layers = atmel_hlcdc_sama5d4_layers, }; @@ -358,19 +370,19 @@ int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, int hback_porch = mode->htotal - mode->hsync_end; int hsync_len = mode->hsync_end - mode->hsync_start;
- if (hsync_len > 0x40 || hsync_len < 1) + if (hsync_len > dc->desc->max_spw + 1 || hsync_len < 1) return MODE_HSYNC;
- if (vsync_len > 0x40 || vsync_len < 1) + if (vsync_len > dc->desc->max_spw + 1 || vsync_len < 1) return MODE_VSYNC;
- if (hfront_porch > 0x200 || hfront_porch < 1 || - hback_porch > 0x200 || hback_porch < 1 || + if (hfront_porch > dc->desc->max_hpw + 1 || hfront_porch < 1 || + hback_porch > dc->desc->max_hpw + 1 || hback_porch < 1 || mode->hdisplay < 1) return MODE_H_ILLEGAL;
- if (vfront_porch > 0x40 || vfront_porch < 1 || - vback_porch > 0x40 || vback_porch < 0 || + if (vfront_porch > dc->desc->max_vpw + 1 || vfront_porch < 1 || + vback_porch > dc->desc->max_vpw || vback_porch < 0 || mode->vdisplay < 1) return MODE_V_ILLEGAL;
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index 6a762c9..864791e 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -50,6 +50,9 @@ * @min_height: minimum height supported by the Display Controller * @max_width: maximum width supported by the Display Controller * @max_height: maximum height supported by the Display Controller + * @max_spw: maximum vertical/horizontal pulse width + * @max_vpw: maximum vertical back/front porch width + * @max_hpw: maximum horizontal back/front porch width * @layers: a layer description table describing available layers * @nlayers: layer description table size */ @@ -58,6 +61,9 @@ struct atmel_hlcdc_dc_desc { int min_height; int max_width; int max_height; + int max_spw; + int max_vpw; + int max_hpw; const struct atmel_hlcdc_layer_desc *layers; int nlayers; };
Le 06/01/2016 11:19, Boris Brezillon a écrit :
The display timings on old SoCs older than the sama5d4 are quite limited and prevent the use of many displays. Add support for extended timing ranges on sama5d2 and sama5d4.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com
Absolutely: Acked-by: Nicolas Ferre nicolas.ferre@atmel.com
Thanks.
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 24 ++++++++++++++++++------ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 6 ++++++ 2 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index d760475..66f97e1 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -50,6 +50,9 @@ static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9n12 = { .min_height = 0, .max_width = 1280, .max_height = 860,
- .max_spw = 0x3f,
- .max_vpw = 0x3f,
- .max_hpw = 0xff, .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9n12_layers), .layers = atmel_hlcdc_at91sam9n12_layers,
}; @@ -134,6 +137,9 @@ static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9x5 = { .min_height = 0, .max_width = 800, .max_height = 600,
- .max_spw = 0x3f,
- .max_vpw = 0x3f,
- .max_hpw = 0xff, .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9x5_layers), .layers = atmel_hlcdc_at91sam9x5_layers,
}; @@ -237,6 +243,9 @@ static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = { .min_height = 0, .max_width = 2048, .max_height = 2048,
- .max_spw = 0x3f,
- .max_vpw = 0x3f,
- .max_hpw = 0x1ff, .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers), .layers = atmel_hlcdc_sama5d3_layers,
}; @@ -320,6 +329,9 @@ static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d4 = { .min_height = 0, .max_width = 2048, .max_height = 2048,
- .max_spw = 0xff,
- .max_vpw = 0xff,
- .max_hpw = 0x3ff, .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d4_layers), .layers = atmel_hlcdc_sama5d4_layers,
}; @@ -358,19 +370,19 @@ int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, int hback_porch = mode->htotal - mode->hsync_end; int hsync_len = mode->hsync_end - mode->hsync_start;
- if (hsync_len > 0x40 || hsync_len < 1)
- if (hsync_len > dc->desc->max_spw + 1 || hsync_len < 1) return MODE_HSYNC;
- if (vsync_len > 0x40 || vsync_len < 1)
- if (vsync_len > dc->desc->max_spw + 1 || vsync_len < 1) return MODE_VSYNC;
- if (hfront_porch > 0x200 || hfront_porch < 1 ||
hback_porch > 0x200 || hback_porch < 1 ||
- if (hfront_porch > dc->desc->max_hpw + 1 || hfront_porch < 1 ||
return MODE_H_ILLEGAL;hback_porch > dc->desc->max_hpw + 1 || hback_porch < 1 || mode->hdisplay < 1)
- if (vfront_porch > 0x40 || vfront_porch < 1 ||
vback_porch > 0x40 || vback_porch < 0 ||
- if (vfront_porch > dc->desc->max_vpw + 1 || vfront_porch < 1 ||
return MODE_V_ILLEGAL;vback_porch > dc->desc->max_vpw || vback_porch < 0 || mode->vdisplay < 1)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index 6a762c9..864791e 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -50,6 +50,9 @@
- @min_height: minimum height supported by the Display Controller
- @max_width: maximum width supported by the Display Controller
- @max_height: maximum height supported by the Display Controller
- @max_spw: maximum vertical/horizontal pulse width
- @max_vpw: maximum vertical back/front porch width
*/
- @max_hpw: maximum horizontal back/front porch width
- @layers: a layer description table describing available layers
- @nlayers: layer description table size
@@ -58,6 +61,9 @@ struct atmel_hlcdc_dc_desc { int min_height; int max_width; int max_height;
- int max_spw;
- int max_vpw;
- int max_hpw; const struct atmel_hlcdc_layer_desc *layers; int nlayers;
};
In order to support multiple outputs we need to move the output mode selection to the CRTC object, so that the output validity check can be done against the drm_atomic_state.
If the connectors selected by a specific mode setting are requiring incompatible bus format the atomic operation is aborted (->atomic_check() returns -EINVAL).
In order to implement that, we need to define our own CRTC state and overload default ->reset(), ->atomic_duplicate_state() and ->atomic_destroy_state() functions.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 140 ++++++++++++++++++++++- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 3 + drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 3 + drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 46 -------- 4 files changed, 142 insertions(+), 50 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index 468a14f..be30bd2 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -32,6 +32,23 @@ #include "atmel_hlcdc_dc.h"
/** + * Atmel HLCDC CRTC state structure + * + * @base: base CRTC state + * @output_mode: RGBXXX output mode + */ +struct atmel_hlcdc_crtc_state { + struct drm_crtc_state base; + unsigned int output_mode; +}; + +static inline struct atmel_hlcdc_crtc_state * +drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state *state) +{ + return container_of(state, struct atmel_hlcdc_crtc_state, base); +} + +/** * Atmel HLCDC CRTC structure * * @base: base DRM CRTC structure @@ -59,6 +76,7 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); struct regmap *regmap = crtc->dc->hlcdc->regmap; struct drm_display_mode *adj = &c->state->adjusted_mode; + struct atmel_hlcdc_crtc_state *state; unsigned long mode_rate; struct videomode vm; unsigned long prate; @@ -112,12 +130,15 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) if (adj->flags & DRM_MODE_FLAG_NHSYNC) cfg |= ATMEL_HLCDC_HSPOL;
+ state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state); + cfg |= state->output_mode << 8; + regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5), ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL | ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE | ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY | ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO | - ATMEL_HLCDC_GUARDTIME_MASK, + ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK, cfg); }
@@ -228,14 +249,78 @@ void atmel_hlcdc_crtc_resume(struct drm_crtc *c) } }
+#define ATMEL_HLCDC_RGB444_OUTPUT BIT(0) +#define ATMEL_HLCDC_RGB565_OUTPUT BIT(1) +#define ATMEL_HLCDC_RGB666_OUTPUT BIT(2) +#define ATMEL_HLCDC_RGB888_OUTPUT BIT(3) +#define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0) + +static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) +{ + unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK; + struct atmel_hlcdc_crtc_state *hstate; + struct drm_connector_state *cstate; + struct drm_connector *connector; + struct atmel_hlcdc_crtc *crtc; + int i; + + crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc); + + for_each_connector_in_state(state->state, connector, cstate, i) { + struct drm_display_info *info = &connector->display_info; + unsigned int supported_fmts = 0; + int j; + + if (!cstate->crtc) + continue; + + for (j = 0; j < info->num_bus_formats; j++) { + switch (info->bus_formats[j]) { + case MEDIA_BUS_FMT_RGB444_1X12: + supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB565_1X16: + supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB666_1X18: + supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; + break; + default: + break; + } + } + + if (crtc->dc->desc->conflicting_output_formats) + output_fmts &= supported_fmts; + else + output_fmts |= supported_fmts; + } + + if (!output_fmts) + return -EINVAL; + + hstate = drm_crtc_state_to_atmel_hlcdc_crtc_state(state); + hstate->output_mode = fls(output_fmts) - 1; + + return 0; +} + static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c, struct drm_crtc_state *s) { struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); + int ret;
if (atmel_hlcdc_dc_mode_valid(crtc->dc, &s->adjusted_mode) != MODE_OK) return -EINVAL;
+ ret = atmel_hlcdc_crtc_select_output_mode(s); + if (ret) + return ret; + return atmel_hlcdc_plane_prepare_disc_area(s); }
@@ -318,13 +403,60 @@ void atmel_hlcdc_crtc_irq(struct drm_crtc *c) atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c)); }
+void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc) +{ + struct atmel_hlcdc_crtc_state *state; + + if (crtc->state && crtc->state->mode_blob) + drm_property_unreference_blob(crtc->state->mode_blob); + + if (crtc->state) { + state = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state); + kfree(state); + } + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) { + crtc->state = &state->base; + crtc->state->crtc = crtc; + } +} + +static struct drm_crtc_state * +atmel_hlcdc_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct atmel_hlcdc_crtc_state *state, *cur; + + if (WARN_ON(!crtc->state)) + return NULL; + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); + + cur = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state); + state->output_mode = cur->output_mode; + + return &state->base; +} + +static void atmel_hlcdc_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *s) +{ + struct atmel_hlcdc_crtc_state *state; + + state = drm_crtc_state_to_atmel_hlcdc_crtc_state(s); + __drm_atomic_helper_crtc_destroy_state(crtc, s); + kfree(state); +} + static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = { .page_flip = drm_atomic_helper_page_flip, .set_config = drm_atomic_helper_set_config, .destroy = atmel_hlcdc_crtc_destroy, - .reset = drm_atomic_helper_crtc_reset, - .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .reset = atmel_hlcdc_crtc_reset, + .atomic_duplicate_state = atmel_hlcdc_crtc_duplicate_state, + .atomic_destroy_state = atmel_hlcdc_crtc_destroy_state, };
int atmel_hlcdc_crtc_create(struct drm_device *dev) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 66f97e1..3f8bfa6 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -53,6 +53,7 @@ static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9n12 = { .max_spw = 0x3f, .max_vpw = 0x3f, .max_hpw = 0xff, + .conflicting_output_formats = true, .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9n12_layers), .layers = atmel_hlcdc_at91sam9n12_layers, }; @@ -140,6 +141,7 @@ static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9x5 = { .max_spw = 0x3f, .max_vpw = 0x3f, .max_hpw = 0xff, + .conflicting_output_formats = true, .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9x5_layers), .layers = atmel_hlcdc_at91sam9x5_layers, }; @@ -246,6 +248,7 @@ static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = { .max_spw = 0x3f, .max_vpw = 0x3f, .max_hpw = 0x1ff, + .conflicting_output_formats = true, .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers), .layers = atmel_hlcdc_sama5d3_layers, }; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index 864791e..eabf9c6 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -53,6 +53,8 @@ * @max_spw: maximum vertical/horizontal pulse width * @max_vpw: maximum vertical back/front porch width * @max_hpw: maximum horizontal back/front porch width + * @conflicting_output_formats: true if RGBXXX output formats conflict with + * each other. * @layers: a layer description table describing available layers * @nlayers: layer description table size */ @@ -64,6 +66,7 @@ struct atmel_hlcdc_dc_desc { int max_spw; int max_vpw; int max_hpw; + bool conflicting_output_formats; const struct atmel_hlcdc_layer_desc *layers; int nlayers; }; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 2255dc9..75237b5 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -27,16 +27,6 @@ #include "atmel_hlcdc_dc.h"
/** - * Atmel HLCDC RGB output mode - */ -enum atmel_hlcdc_connector_rgb_mode { - ATMEL_HLCDC_CONNECTOR_RGB444, - ATMEL_HLCDC_CONNECTOR_RGB565, - ATMEL_HLCDC_CONNECTOR_RGB666, - ATMEL_HLCDC_CONNECTOR_RGB888, -}; - -/** * Atmel HLCDC RGB connector structure * * This structure stores RGB slave device information. @@ -89,7 +79,6 @@ static void atmel_hlcdc_panel_encoder_enable(struct drm_encoder *encoder) 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); - drm_panel_enable(panel->panel); }
@@ -102,42 +91,7 @@ static void atmel_hlcdc_panel_encoder_disable(struct drm_encoder *encoder) drm_panel_disable(panel->panel); }
-static void -atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted) -{ - struct atmel_hlcdc_rgb_output *rgb = - drm_encoder_to_atmel_hlcdc_rgb_output(encoder); - struct drm_display_info *info = &rgb->connector.display_info; - unsigned int cfg; - - cfg = 0; - - if (info->num_bus_formats) { - switch (info->bus_formats[0]) { - case MEDIA_BUS_FMT_RGB565_1X16: - cfg |= ATMEL_HLCDC_CONNECTOR_RGB565 << 8; - break; - case MEDIA_BUS_FMT_RGB666_1X18: - cfg |= ATMEL_HLCDC_CONNECTOR_RGB666 << 8; - break; - case MEDIA_BUS_FMT_RGB888_1X24: - cfg |= ATMEL_HLCDC_CONNECTOR_RGB888 << 8; - break; - case MEDIA_BUS_FMT_RGB444_1X12: - default: - break; - } - } - - regmap_update_bits(rgb->dc->hlcdc->regmap, ATMEL_HLCDC_CFG(5), - ATMEL_HLCDC_MODE_MASK, - cfg); -} - static const struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = { - .mode_set = atmel_hlcdc_rgb_encoder_mode_set, .disable = atmel_hlcdc_panel_encoder_disable, .enable = atmel_hlcdc_panel_encoder_enable, };
The current output code only supports connection to drm panels. First simplify the drm panel code, and then add support for external drm bridges.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 2 +- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 193 +++++++++++++---------- 2 files changed, 113 insertions(+), 82 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 3f8bfa6..d004751 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -551,7 +551,7 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
ret = atmel_hlcdc_create_outputs(dev); if (ret) { - dev_err(dev->dev, "failed to create panel: %d\n", ret); + dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret); return ret; }
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 75237b5..39802c0 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -34,11 +34,13 @@ * @connector: DRM connector * @encoder: DRM encoder * @dc: pointer to the atmel_hlcdc_dc structure + * @panel: panel connected on the RGB output */ struct atmel_hlcdc_rgb_output { struct drm_connector connector; struct drm_encoder encoder; struct atmel_hlcdc_dc *dc; + struct drm_panel *panel; };
static inline struct atmel_hlcdc_rgb_output * @@ -54,46 +56,31 @@ drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder) return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder); }
-/** - * Atmel HLCDC Panel device structure - * - * This structure is specialization of the slave device structure to - * interface with drm panels. - * - * @base: base slave device fields - * @panel: drm panel attached to this slave device - */ -struct atmel_hlcdc_panel { - struct atmel_hlcdc_rgb_output base; - struct drm_panel *panel; -}; - -static inline struct atmel_hlcdc_panel * -atmel_hlcdc_rgb_output_to_panel(struct atmel_hlcdc_rgb_output *output) -{ - return container_of(output, struct atmel_hlcdc_panel, base); -} - -static void atmel_hlcdc_panel_encoder_enable(struct drm_encoder *encoder) +static void atmel_hlcdc_rgb_encoder_enable(struct drm_encoder *encoder) { 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); - drm_panel_enable(panel->panel); + + if (rgb->panel) { + drm_panel_prepare(rgb->panel); + drm_panel_enable(rgb->panel); + } }
-static void atmel_hlcdc_panel_encoder_disable(struct drm_encoder *encoder) +static void atmel_hlcdc_rgb_encoder_disable(struct drm_encoder *encoder) { 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);
- drm_panel_disable(panel->panel); + if (rgb->panel) { + drm_panel_disable(rgb->panel); + drm_panel_unprepare(rgb->panel); + } }
static const struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = { - .disable = atmel_hlcdc_panel_encoder_disable, - .enable = atmel_hlcdc_panel_encoder_enable, + .disable = atmel_hlcdc_rgb_encoder_disable, + .enable = atmel_hlcdc_rgb_encoder_enable, };
static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder) @@ -110,9 +97,11 @@ static int atmel_hlcdc_panel_get_modes(struct drm_connector *connector) { struct atmel_hlcdc_rgb_output *rgb = drm_connector_to_atmel_hlcdc_rgb_output(connector); - struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
- return panel->panel->funcs->get_modes(panel->panel); + if (rgb->panel) + return rgb->panel->funcs->get_modes(rgb->panel); + + return 0; }
static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector, @@ -144,7 +133,13 @@ static const struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helpe static enum drm_connector_status atmel_hlcdc_panel_connector_detect(struct drm_connector *connector, bool force) { - return connector_status_connected; + struct atmel_hlcdc_rgb_output *rgb = + drm_connector_to_atmel_hlcdc_rgb_output(connector); + + if (rgb->panel) + return connector_status_connected; + + return connector_status_disconnected; }
static void @@ -152,9 +147,10 @@ atmel_hlcdc_panel_connector_destroy(struct drm_connector *connector) { struct atmel_hlcdc_rgb_output *rgb = drm_connector_to_atmel_hlcdc_rgb_output(connector); - struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
- drm_panel_detach(panel->panel); + if (rgb->panel) + drm_panel_detach(rgb->panel); + drm_connector_cleanup(connector); }
@@ -168,87 +164,122 @@ static const struct drm_connector_funcs atmel_hlcdc_panel_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, };
-static int atmel_hlcdc_create_panel_output(struct drm_device *dev, - struct of_endpoint *ep) +static int atmel_hlcdc_check_endpoint(struct drm_device *dev, + const struct of_endpoint *ep) { - struct atmel_hlcdc_dc *dc = dev->dev_private; struct device_node *np; - struct drm_panel *p = NULL; - struct atmel_hlcdc_panel *panel; - int ret; + void *obj;
np = of_graph_get_remote_port_parent(ep->local_node); - if (!np) - return -EINVAL;
- p = of_drm_find_panel(np); + obj = of_drm_find_panel(np); + if (!obj) + obj = of_drm_find_bridge(np); + of_node_put(np);
- if (!p) - return -EPROBE_DEFER; + return obj ? 0 : -EPROBE_DEFER; +}
- panel = devm_kzalloc(dev->dev, sizeof(*panel), GFP_KERNEL); - if (!panel) - return -EINVAL; +static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, + const struct of_endpoint *ep) +{ + struct atmel_hlcdc_dc *dc = dev->dev_private; + struct atmel_hlcdc_rgb_output *output; + struct device_node *np; + struct drm_panel *panel; + struct drm_bridge *bridge; + int ret;
+ output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL); + if (!output) + return -EINVAL;
- panel->base.dc = dc; + output->dc = dc;
- drm_encoder_helper_add(&panel->base.encoder, + drm_encoder_helper_add(&output->encoder, &atmel_hlcdc_panel_encoder_helper_funcs); - ret = drm_encoder_init(dev, &panel->base.encoder, + ret = drm_encoder_init(dev, &output->encoder, &atmel_hlcdc_panel_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL); if (ret) return ret;
- panel->base.connector.dpms = DRM_MODE_DPMS_OFF; - panel->base.connector.polled = DRM_CONNECTOR_POLL_CONNECT; - drm_connector_helper_add(&panel->base.connector, - &atmel_hlcdc_panel_connector_helper_funcs); - ret = drm_connector_init(dev, &panel->base.connector, - &atmel_hlcdc_panel_connector_funcs, - DRM_MODE_CONNECTOR_Unknown); - if (ret) - goto err_encoder_cleanup; + output->encoder.possible_crtcs = 0x1; + + np = of_graph_get_remote_port_parent(ep->local_node);
- drm_mode_connector_attach_encoder(&panel->base.connector, - &panel->base.encoder); - panel->base.encoder.possible_crtcs = 0x1; + ret = -EPROBE_DEFER;
- drm_panel_attach(p, &panel->base.connector); - panel->panel = p; + panel = of_drm_find_panel(np); + if (panel) { + of_node_put(np); + output->connector.dpms = DRM_MODE_DPMS_OFF; + output->connector.polled = DRM_CONNECTOR_POLL_CONNECT; + drm_connector_helper_add(&output->connector, + &atmel_hlcdc_panel_connector_helper_funcs); + ret = drm_connector_init(dev, &output->connector, + &atmel_hlcdc_panel_connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + if (ret) + goto err_encoder_cleanup;
- return 0; + drm_mode_connector_attach_encoder(&output->connector, + &output->encoder); + + ret = drm_panel_attach(panel, &output->connector); + if (ret) { + drm_connector_cleanup(&output->connector); + goto err_encoder_cleanup; + } + + output->panel = panel; + + return 0; + } + + bridge = of_drm_find_bridge(np); + of_node_put(np); + + if (bridge) { + output->encoder.bridge = bridge; + bridge->encoder = &output->encoder; + ret = drm_bridge_attach(dev, bridge); + if (!ret) + return 0; + }
err_encoder_cleanup: - drm_encoder_cleanup(&panel->base.encoder); + drm_encoder_cleanup(&output->encoder);
return ret; }
int atmel_hlcdc_create_outputs(struct drm_device *dev) { - struct device_node *port_np, *np; + struct device_node *ep_np = NULL; struct of_endpoint ep; int ret;
- port_np = of_get_child_by_name(dev->dev->of_node, "port"); - if (!port_np) - return -EINVAL; + for_each_endpoint_of_node(dev->dev->of_node, ep_np) { + ret = of_graph_parse_endpoint(ep_np, &ep); + if (!ret) + ret = atmel_hlcdc_check_endpoint(dev, &ep);
- np = of_get_child_by_name(port_np, "endpoint"); - of_node_put(port_np); + of_node_put(ep_np); + if (ret) + return ret; + }
- if (!np) - return -EINVAL; + for_each_endpoint_of_node(dev->dev->of_node, ep_np) { + ret = of_graph_parse_endpoint(ep_np, &ep); + if (!ret) + ret = atmel_hlcdc_attach_endpoint(dev, &ep);
- ret = of_graph_parse_endpoint(np, &ep); - of_node_put(port_np); + of_node_put(ep_np); + if (ret) + return ret; + }
- if (ret) - return ret; - - /* We currently only support panel output */ - return atmel_hlcdc_create_panel_output(dev, &ep); + return 0; }
Move the adjusted display mode check into ->mode_fixup().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index be30bd2..d5a028f 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -142,11 +142,13 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) cfg); }
-static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc, +static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *c, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - return true; + struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); + + return atmel_hlcdc_dc_mode_valid(crtc->dc, adjusted_mode) == MODE_OK; }
static void atmel_hlcdc_crtc_disable(struct drm_crtc *c) @@ -311,12 +313,8 @@ static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c, struct drm_crtc_state *s) { - struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); int ret;
- if (atmel_hlcdc_dc_mode_valid(crtc->dc, &s->adjusted_mode) != MODE_OK) - return -EINVAL; - ret = atmel_hlcdc_crtc_select_output_mode(s); if (ret) return ret;
Le 06/01/2016 11:19, Boris Brezillon a écrit :
Move the adjusted display mode check into ->mode_fixup().
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com
Seems okay: Acked-by: Nicolas Ferre nicolas.ferre@atmel.com
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index be30bd2..d5a028f 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -142,11 +142,13 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) cfg); }
-static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc, +static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *c, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) {
- return true;
- struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
- return atmel_hlcdc_dc_mode_valid(crtc->dc, adjusted_mode) == MODE_OK;
}
static void atmel_hlcdc_crtc_disable(struct drm_crtc *c) @@ -311,12 +313,8 @@ static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c, struct drm_crtc_state *s) {
struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); int ret;
if (atmel_hlcdc_dc_mode_valid(crtc->dc, &s->adjusted_mode) != MODE_OK)
return -EINVAL;
ret = atmel_hlcdc_crtc_select_output_mode(s); if (ret) return ret;
On Wed, 6 Jan 2016 11:19:21 +0100 Boris Brezillon boris.brezillon@free-electrons.com wrote:
Hello,
This series is a collection of fixes and improvements for the atmel-hlcdc driver.
The main feature added here is the support for external RGB -> XXX bridges (patch 6 and 7).
The first patch is a fix preventing a potential memory leak. Patch 2 is adding support for asynchronous mode setting, which was supported before the migration to atomic mode setting.
Patch 3 is just a minor fix to expose the real encoder and connector types (we are currently exposing an LVDS encoder/connector, which is wrong since the display controller output the pixel stream in raw RGB).
Patch 4 is removing useless fields and functions which were left when moving to atomic modesetting.
And patch 8 is just a cosmetic patch moving the mode checking code from ->atomic_check() to ->mode_fixup().
Dave, Daniel, any comment on this series (AKA ping)?
Best Regards,
Boris
Boris Brezillon (8): drm: atmel-hlcdc: add a ->cleanup_fb() operation drm: atmel-hlcdc: support asynchronous atomic commit operations drm: atmel-hlcdc: fix connector and encoder types drm: atmel-hlcdc: remove leftovers from atomic mode setting migration drm: atmel-hlcdc: support extended timing ranges on sama5d4 and sama5d2 drm: atmel-hlcdc: move output mode selection in CRTC implementation drm: atmel-hlcdc: rework the output code to support drm bridges drm: atmel-hlcdc: check display mode validity in crtc->mode_fixup()
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 148 +++++++++++++- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 123 ++++++++++- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 16 ++ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 249 ++++++++++------------- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 22 +- 5 files changed, 400 insertions(+), 158 deletions(-)
On 1 March 2016 at 20:55, Boris Brezillon boris.brezillon@free-electrons.com wrote:
On Wed, 6 Jan 2016 11:19:21 +0100 Boris Brezillon boris.brezillon@free-electrons.com wrote:
Hello,
This series is a collection of fixes and improvements for the atmel-hlcdc driver.
The main feature added here is the support for external RGB -> XXX bridges (patch 6 and 7).
The first patch is a fix preventing a potential memory leak. Patch 2 is adding support for asynchronous mode setting, which was supported before the migration to atomic mode setting.
Patch 3 is just a minor fix to expose the real encoder and connector types (we are currently exposing an LVDS encoder/connector, which is wrong since the display controller output the pixel stream in raw RGB).
Patch 4 is removing useless fields and functions which were left when moving to atomic modesetting.
And patch 8 is just a cosmetic patch moving the mode checking code from ->atomic_check() to ->mode_fixup().
Dave, Daniel, any comment on this series (AKA ping)?
On internal driver stuff, I'd usually only look on a git pull :-)
Dave.
On Thu, 17 Mar 2016 12:43:11 +1000 Dave Airlie airlied@gmail.com wrote:
On 1 March 2016 at 20:55, Boris Brezillon boris.brezillon@free-electrons.com wrote:
On Wed, 6 Jan 2016 11:19:21 +0100 Boris Brezillon boris.brezillon@free-electrons.com wrote:
Hello,
This series is a collection of fixes and improvements for the atmel-hlcdc driver.
The main feature added here is the support for external RGB -> XXX bridges (patch 6 and 7).
The first patch is a fix preventing a potential memory leak. Patch 2 is adding support for asynchronous mode setting, which was supported before the migration to atomic mode setting.
Patch 3 is just a minor fix to expose the real encoder and connector types (we are currently exposing an LVDS encoder/connector, which is wrong since the display controller output the pixel stream in raw RGB).
Patch 4 is removing useless fields and functions which were left when moving to atomic modesetting.
And patch 8 is just a cosmetic patch moving the mode checking code from ->atomic_check() to ->mode_fixup().
Dave, Daniel, any comment on this series (AKA ping)?
On internal driver stuff, I'd usually only look on a git pull :-)
Yep, Daniel told me that both of you usually don't review internal driver changes. I'll send a PR soon (after the merge window of course).
Thanks,
Boris
Le 06/01/2016 11:19, Boris Brezillon a écrit :
Hello,
This series is a collection of fixes and improvements for the atmel-hlcdc driver.
The main feature added here is the support for external RGB -> XXX bridges (patch 6 and 7).
The first patch is a fix preventing a potential memory leak. Patch 2 is adding support for asynchronous mode setting, which was supported before the migration to atomic mode setting.
Patch 3 is just a minor fix to expose the real encoder and connector types (we are currently exposing an LVDS encoder/connector, which is wrong since the display controller output the pixel stream in raw RGB).
Patch 4 is removing useless fields and functions which were left when moving to atomic modesetting.
And patch 8 is just a cosmetic patch moving the mode checking code from ->atomic_check() to ->mode_fixup().
To give more weight to this series, you can also add my:
Tested-by: Nicolas Ferre nicolas.ferre@atmel.com
Bye,
Boris Brezillon (8): drm: atmel-hlcdc: add a ->cleanup_fb() operation drm: atmel-hlcdc: support asynchronous atomic commit operations drm: atmel-hlcdc: fix connector and encoder types drm: atmel-hlcdc: remove leftovers from atomic mode setting migration drm: atmel-hlcdc: support extended timing ranges on sama5d4 and sama5d2 drm: atmel-hlcdc: move output mode selection in CRTC implementation drm: atmel-hlcdc: rework the output code to support drm bridges drm: atmel-hlcdc: check display mode validity in crtc->mode_fixup()
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 148 +++++++++++++- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 123 ++++++++++- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 16 ++ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 249 ++++++++++------------- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 22 +- 5 files changed, 400 insertions(+), 158 deletions(-)
dri-devel@lists.freedesktop.org