[PATCH 03/11] drm/simple-kms-helper: Add drm_simple_connector_create()

Noralf Trønnes noralf at tronnes.org
Fri Jan 25 12:05:50 UTC 2019



Den 24.01.2019 15.53, skrev Hans de Goede:
> Hi,
> 
> On 24-01-19 15:38, Noralf Trønnes wrote:
>> [cc:Hans]
>>
>> Den 21.01.2019 10.22, skrev Daniel Vetter:
>>> On Sun, Jan 20, 2019 at 12:43:10PM +0100, Noralf Trønnes wrote:
>>>> This adds a function that creates a simple connector that has only one
>>>> static mode. Additionally add a helper to set &drm_mode_config width
>>>> and height from the static mode.
>>>>
>>>> Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
>>>> ---
>>>>   drivers/gpu/drm/drm_simple_kms_helper.c | 122
>>>> ++++++++++++++++++++++++
>>>>   include/drm/drm_simple_kms_helper.h     |   6 ++
>>>>   2 files changed, 128 insertions(+)
>>>>
>>>> diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c
>>>> b/drivers/gpu/drm/drm_simple_kms_helper.c
>>>> index 917812448d1b..ca29975afefe 100644
>>>> --- a/drivers/gpu/drm/drm_simple_kms_helper.c
>>>> +++ b/drivers/gpu/drm/drm_simple_kms_helper.c
>>>> @@ -11,6 +11,8 @@
>>>>   #include <drm/drm_atomic.h>
>>>>   #include <drm/drm_atomic_helper.h>
>>>>   #include <drm/drm_crtc_helper.h>
>>>> +#include <drm/drm_device.h>
>>>> +#include <drm/drm_modes.h>
>>>>   #include <drm/drm_plane_helper.h>
>>>>   #include <drm/drm_simple_kms_helper.h>
>>>>   #include <linux/slab.h>
>>>> @@ -299,4 +301,124 @@ int drm_simple_display_pipe_init(struct
>>>> drm_device *dev,
>>>>   }
>>>>   EXPORT_SYMBOL(drm_simple_display_pipe_init);
>>>>   +static const struct drm_connector_helper_funcs
>>>> drm_simple_connector_hfuncs = {
>>>> +    /* dummy for the atomic helper */
>>>> +};
>>>> +
>>>> +static int drm_simple_connector_fill_modes(struct drm_connector
>>>> *connector,
>>>> +                       uint32_t maxX, uint32_t maxY)
>>>> +{
>>>> +    return 1;
>>>> +}
>>>> +
>>>> +static void drm_simple_connector_destroy(struct drm_connector
>>>> *connector)
>>>> +{
>>>> +    drm_connector_cleanup(connector);
>>>> +    kfree(connector);
>>>> +}
>>>> +
>>>> +static const struct drm_connector_funcs drm_simple_connector_funcs = {
>>>> +    .reset = drm_atomic_helper_connector_reset,
>>>> +    .fill_modes = drm_simple_connector_fill_modes,
>>>> +    .destroy = drm_simple_connector_destroy,
>>>> +    .atomic_duplicate_state =
>>>> drm_atomic_helper_connector_duplicate_state,
>>>> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>>>> +};
>>>> +
>>>> +/**
>>>> + * drm_simple_connector_create - Create a connector with one static
>>>> mode
>>>> + * @dev: DRM device
>>>> + * @connector_type: Connector type
>>>> + * @mode: Supported display mode
>>>> + * @rotation: Initial @mode rotation in degrees
>>>
>>> We have rotation properties for this, pls don't use degress here.
>>>
>>
>> This rotation represents the way the display is mounted in the casing.
>> It is configured using a DT property:
>>
>> Documentation/devicetree/bindings/display/panel/panel.txt:
>> - rotation:    Display rotation in degrees counter clockwise
>> (0,90,180,270)
>>
>> In the driver I set up a display mode which is rotated to match how it's
>> mounted:
>>
>> static const struct drm_display_mode mode = {
>>     DRM_SIMPLE_MODE(320, 240, 58, 43),
>> };
>>
>>     device_property_read_u32(dev, "rotation", &rotation);
>>
>>     connector = drm_simple_connector_create(drm,
>> DRM_MODE_CONNECTOR_VIRTUAL, &mode, rotation);
>>
>>
>> The display controller is configured to scan out according to this
>> rotation.
> 
> That sounds wrong, this sounds like you're trying to hide the fact
> that the LCD display is not mounted upright from userspace and
> transparently deal with this. 

Indeed that's what I'm doing.

>                               This is what I wanted to do at
> first too, but in the end it turns out that that has a bunch
> of issues.
> 
> This was extensively discussed and it was decided that this is not
> something which we want to do because it gets us into trouble with
> things like overlay planes, etc. Also many devices only support
> 90 / 270 degrees rotation if the framebuffer is tiled in a specific
> way, so if we do the rotation transparently, what do we do then
> if userspace attaches an incompatible framebuffer to the CRTC?
> 
> Instead the decision was made to provide a property on the drm-connector
> which tells userspace that the LCD panel is not mounted upright and then
> let userspace deal with this.
> 
>> It's important that the display mode matches the case mounting because
>> of fbdev. fbdev userspace has very limited support for rotation. Most
>> utilities have no support for it.
> 
> Right, so maybe it is time for userspace to move to the kms API already?
> 

These tiny SPI displays are heavily used in the Maker community which is
my main user base. Most are not professionals and fbdev is easy to use,
so I want to avoid crippling fbdev.

> Note that that will not fix things by itself, but at least it makes
> the info that the display is not upright available to userspace.
> 
> Note I've already patched both plymouth and mutter to use the
> drm-connector property for this.
> 
>> The work Hans did seems to only care about fbcon
> 
> Since fbcon still uses fbdev, and since it already supported
> rendering the console rotated I added some glue code to make
> it automatically do the right thing for non-upright LCD-panels.
> 
> As mentioned I did also fix the userspace kms apps which most
> distros use as their default desktop components.
> 
>> and it doesn't support 90/270 hw rotation.
> 
> Right, because of the framebuffer tiling thing.
> 

If we instead apply the rotation after the framebuffer is created, is it
enough to check fb->modifier == DRM_FORMAT_MOD_LINEAR to see if it's ok
to rotate?

My next task after this patchset is to try and move the modesetting code
from drm_fb_helper to drm_client. I see now that I have to take this
rotation into account as well for the future bootsplash client to show
correctly.

Noralf.

> So building on top of my work, how this should work is that
> the modeline for the display reflect the actual hardware LCD
> modeline, so if it is say a 720x1280 portrait screen mounted
> in landscape mode, then the modeline will be 720x1280 as that
> is what is actually going over the wire to the panel/display.
> 
> And the device-tree rotation property can be passed to
> drm_connector_init_panel_orientation_property() to let
> fbcon and kms API user know they need to render rotated.
> 
> Note that kms API users can use hardware rotation to deal
> with this if they so desire.
> 
> Regards,
> 
> Hans
> 
> 
> 
> 
> 
> 
> 
>>
>> Noralf.
>>
>>> Also would be good to wire this up with the rotation property Hans added
>>> recently, for pre-rotated screens (see the kerneldoc for "panel
>>> orientation" and drm_connector_init_panel_orientation_property()). So
>>> that
>>> userspace knows how to rotate its rendering to make everything look
>>> correct in the end again.
>>>
>>>
>>>> + *
>>>> + * This function creates a &drm_connector that has one fixed
>>>> &drm_display_mode
>>>> + * which will be rotated according to @rotation.
>>>
>>>  From a functionality pov this is very close to a panel wrapped into a
>>> bridge. I think it would be good to differentiate a bit between these
>>> two
>>> cases more. After all there was a really long discussion about how the
>>> panel stuff does or does not exactly fit for tinydrm, would be good to
>>> summarize this here and at least point at drm_panel_bridge_add().
>>>
>>> Also would be good to explain in the overview DOC comment that this is a
>>> fully independent part of the simple helpers, and drivers can use one or
>>> the other.
>>> -Daniel
>>>
>>>> + *
>>>> + * Returns:
>>>> + * Pointer to connector on success, or ERR_PTR on failure.
>>>> + */
>>>> +struct drm_connector *
>>>> +drm_simple_connector_create(struct drm_device *dev, int
>>>> connector_type,
>>>> +                const struct drm_display_mode *mode,
>>>> +                unsigned int rotation)
>>>> +{
>>>> +    struct drm_display_mode *mode_dup = NULL;
>>>> +    struct drm_connector *connector;
>>>> +    int ret;
>>>> +
>>>> +    connector = kzalloc(sizeof(*connector), GFP_KERNEL);
>>>> +    if (!connector)
>>>> +        return ERR_PTR(-ENOMEM);
>>>> +
>>>> +    drm_connector_helper_add(connector, &drm_simple_connector_hfuncs);
>>>> +    ret = drm_connector_init(dev, connector,
>>>> &drm_simple_connector_funcs,
>>>> +                 connector_type);
>>>> +    if (ret)
>>>> +        goto err_free;
>>>> +
>>>> +    connector->status = connector_status_connected;
>>>> +
>>>> +    mode_dup = drm_mode_duplicate(dev, mode);
>>>> +    if (!mode_dup) {
>>>> +        ret = -ENOMEM;
>>>> +        goto err_cleanup;
>>>> +    }
>>>> +
>>>> +    if (rotation == 90 || rotation == 270) {
>>>> +        swap(mode_dup->hdisplay, mode_dup->vdisplay);
>>>> +        swap(mode_dup->hsync_start, mode_dup->vsync_start);
>>>> +        swap(mode_dup->hsync_end, mode_dup->vsync_end);
>>>> +        swap(mode_dup->htotal, mode_dup->vtotal);
>>>> +        swap(mode_dup->width_mm, mode_dup->height_mm);
>>>> +    } else if (rotation != 0 && rotation != 180) {
>>>> +        DRM_ERROR("Illegal rotation value %u\n", rotation);
>>>> +        ret = -EINVAL;
>>>> +        goto err_cleanup;
>>>> +    }
>>>> +
>>>> +    mode_dup->type |= DRM_MODE_TYPE_PREFERRED;
>>>> +    if (mode_dup->name[0] == '\0')
>>>> +        drm_mode_set_name(mode_dup);
>>>> +
>>>> +    list_add(&mode_dup->head, &connector->modes);
>>>> +
>>>> +    connector->display_info.width_mm = mode_dup->width_mm;
>>>> +    connector->display_info.height_mm = mode_dup->height_mm;
>>>> +
>>>> +    return connector;
>>>> +
>>>> +err_cleanup:
>>>> +    drm_connector_cleanup(connector);
>>>> +    drm_mode_destroy(dev, mode_dup);
>>>> +err_free:
>>>> +    kfree(connector);
>>>> +
>>>> +    return ERR_PTR(ret);
>>>> +}
>>>> +EXPORT_SYMBOL(drm_simple_connector_create);
>>>> +
>>>> +/**
>>>> + * drm_simple_connector_set_mode_config - Set &drm_mode_config
>>>> width and height
>>>> + * @connector: Connector
>>>> + *
>>>> + * This function sets the &drm_mode_config min/max width and height
>>>> based on the
>>>> + * connector fixed display mode.
>>>> + */
>>>> +void drm_simple_connector_set_mode_config(struct drm_connector
>>>> *connector)
>>>> +{
>>>> +    struct drm_mode_config *mode_config =
>>>> &connector->dev->mode_config;
>>>> +    struct drm_display_mode *mode;
>>>> +
>>>> +    mode = list_first_entry(&connector->modes, struct
>>>> drm_display_mode, head);
>>>> +    if (WARN_ON(!mode))
>>>> +        return;
>>>> +
>>>> +    mode_config->min_width = mode->hdisplay;
>>>> +    mode_config->max_width = mode->hdisplay;
>>>> +    mode_config->min_height = mode->vdisplay;
>>>> +    mode_config->max_height = mode->vdisplay;
>>>> +}
>>>> +EXPORT_SYMBOL(drm_simple_connector_set_mode_config);
>>>> +
>>>>   MODULE_LICENSE("GPL");
>>>> diff --git a/include/drm/drm_simple_kms_helper.h
>>>> b/include/drm/drm_simple_kms_helper.h
>>>> index 451960438a29..ab3d847b7713 100644
>>>> --- a/include/drm/drm_simple_kms_helper.h
>>>> +++ b/include/drm/drm_simple_kms_helper.h
>>>> @@ -182,4 +182,10 @@ int drm_simple_display_pipe_init(struct
>>>> drm_device *dev,
>>>>               const uint64_t *format_modifiers,
>>>>               struct drm_connector *connector);
>>>>   +struct drm_connector *
>>>> +drm_simple_connector_create(struct drm_device *dev, int
>>>> connector_type,
>>>> +                const struct drm_display_mode *mode,
>>>> +                unsigned int rotation);
>>>> +void drm_simple_connector_set_mode_config(struct drm_connector
>>>> *connector);
>>>> +
>>>>   #endif /* __LINUX_DRM_SIMPLE_KMS_HELPER_H */
>>>> -- 
>>>> 2.20.1
>>>>
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel at lists.freedesktop.org
>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>


More information about the dri-devel mailing list