diff mbox series

[v4,2/3] vkms: Add support for multiple pipes

Message ID 20230426104059.28732-3-marius.vlad@collabora.com (mailing list archive)
State New, archived
Headers show
Series drm/vkms: Add support for multiple pipes | expand

Commit Message

Marius Vlad April 26, 2023, 10:40 a.m. UTC
This adds support for creating multiple virtual pipes, in case one would
need to display multiple independent things on different outputs.

We use a module parameter to specify how many to create,  defaulting to
just one with the possibility of specifying a maximum of 4 possible pipes.

This is of particular importance when testing out the DRM back-end in
compositors, but also to be able to independently set different DPMS states.

Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
---
 drivers/gpu/drm/vkms/vkms_crtc.c      |  3 +--
 drivers/gpu/drm/vkms/vkms_drv.c       | 38 ++++++++++++++++++++++-----
 drivers/gpu/drm/vkms/vkms_drv.h       | 12 ++++++---
 drivers/gpu/drm/vkms/vkms_output.c    |  5 ++--
 drivers/gpu/drm/vkms/vkms_writeback.c | 24 ++++++++---------
 5 files changed, 55 insertions(+), 27 deletions(-)

Comments

Maíra Canal May 4, 2023, 12:20 p.m. UTC | #1
Hi Marius,

I'm not sure if you'll keep working in this series after the proposal of
the Chromium folks. Anyway, just a small comment.

On 4/26/23 07:40, Marius Vlad wrote:
> This adds support for creating multiple virtual pipes, in case one would
> need to display multiple independent things on different outputs.
> 
> We use a module parameter to specify how many to create,  defaulting to
> just one with the possibility of specifying a maximum of 4 possible pipes.
> 
> This is of particular importance when testing out the DRM back-end in
> compositors, but also to be able to independently set different DPMS states.
> 
> Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
> ---
>   drivers/gpu/drm/vkms/vkms_crtc.c      |  3 +--
>   drivers/gpu/drm/vkms/vkms_drv.c       | 38 ++++++++++++++++++++++-----
>   drivers/gpu/drm/vkms/vkms_drv.h       | 12 ++++++---
>   drivers/gpu/drm/vkms/vkms_output.c    |  5 ++--
>   drivers/gpu/drm/vkms/vkms_writeback.c | 24 ++++++++---------
>   5 files changed, 55 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
> index 515f6772b866..051e7b647db9 100644
> --- a/drivers/gpu/drm/vkms/vkms_crtc.c
> +++ b/drivers/gpu/drm/vkms/vkms_crtc.c
> @@ -89,8 +89,7 @@ static bool vkms_get_vblank_timestamp(struct drm_crtc *crtc,
>   {
>   	struct drm_device *dev = crtc->dev;
>   	unsigned int pipe = crtc->index;
> -	struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
> -	struct vkms_output *output = &vkmsdev->output;
> +	struct vkms_output *output = drm_crtc_to_vkms_output(crtc);
>   	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
>   
>   	if (!READ_ONCE(vblank->enabled)) {
> diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
> index e3c9c9571c8d..0ee17065bc6f 100644
> --- a/drivers/gpu/drm/vkms/vkms_drv.c
> +++ b/drivers/gpu/drm/vkms/vkms_drv.c

[...]

> @@ -230,7 +243,18 @@ static int __init vkms_init(void)
>   	config->cursor = enable_cursor;
>   	config->writeback = enable_writeback;
>   	config->overlay = enable_overlay;
> -
> +	config->max_pipes = max_pipes;
> +	if (config->max_pipes > NUM_MAX_PIPES)
> +		config->max_pipes = NUM_MAX_PIPES;
> +	if (config->max_pipes < NUM_MIN_PIPES)
> +		config->max_pipes = NUM_MIN_PIPES;
> +
> +	/* with 8 overlays and 4 pipes we exceed the amount of planes we
> +	 * have; see __drm_universal_plane_init() */
> +	if (config->overlay && config->max_pipes == NUM_MAX_PIPES)
> +		config->max_pipes = NUM_MAX_PIPES - 1;

Instead of reducing the number of pipes, you could change the maximum number
of overlay planes to 6-4 planes. So many overlay planes are not that needed
and it would be nice to have 4 pipes with overlay planes enabled.

Best Regards,
- Maíra Canal

> +
> +	DRM_INFO("Initialized with %u pipes\n", config->max_pipes);
>   	ret = vkms_create(config);
>   	if (ret)
>   		kfree(config);
> diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
> index f152d54baf76..b096832ed0f2 100644
> --- a/drivers/gpu/drm/vkms/vkms_drv.h
> +++ b/drivers/gpu/drm/vkms/vkms_drv.h
> @@ -21,7 +21,9 @@
>   #define XRES_MAX  8192
>   #define YRES_MAX  8192
>   
> -#define NUM_OVERLAY_PLANES 8
> +#define NUM_OVERLAY_PLANES	8
> +#define NUM_MAX_PIPES		4
> +#define NUM_MIN_PIPES		1
>   
>   struct vkms_frame_info {
>   	struct drm_framebuffer *fb;
> @@ -112,6 +114,7 @@ struct vkms_config {
>   	bool writeback;
>   	bool cursor;
>   	bool overlay;
> +	unsigned int max_pipes;
>   	/* only set when instantiated */
>   	struct vkms_device *dev;
>   };
> @@ -119,13 +122,16 @@ struct vkms_config {
>   struct vkms_device {
>   	struct drm_device drm;
>   	struct platform_device *platform;
> -	struct vkms_output output;
> +	struct vkms_output output[NUM_MAX_PIPES];
>   	const struct vkms_config *config;
>   };
>   
>   #define drm_crtc_to_vkms_output(target) \
>   	container_of(target, struct vkms_output, crtc)
>   
> +#define wb_connector_to_vkms_output(target) \
> +	container_of(target, struct vkms_output, wb_connector)
> +
>   #define drm_device_to_vkms_device(target) \
>   	container_of(target, struct vkms_device, drm)
>   
> @@ -157,6 +163,6 @@ void vkms_set_composer(struct vkms_output *out, bool enabled);
>   void vkms_compose_row(struct line_buffer *stage_buffer, struct vkms_plane_state *plane, int y);
>   
>   /* Writeback */
> -int vkms_enable_writeback_connector(struct vkms_device *vkmsdev);
> +int vkms_enable_writeback_connector(struct vkms_device *vkmsdev, int index);
>   
>   #endif /* _VKMS_DRV_H_ */
> diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
> index 1b8aa3a34e46..9c801b8c4c12 100644
> --- a/drivers/gpu/drm/vkms/vkms_output.c
> +++ b/drivers/gpu/drm/vkms/vkms_output.c
> @@ -48,7 +48,7 @@ static int vkms_add_overlay_plane(struct vkms_device *vkmsdev, int index,
>   
>   int vkms_output_init(struct vkms_device *vkmsdev, int index)
>   {
> -	struct vkms_output *output = &vkmsdev->output;
> +	struct vkms_output *output = &vkmsdev->output[index];
>   	struct drm_device *dev = &vkmsdev->drm;
>   	struct drm_connector *connector = &output->connector;
>   	struct drm_encoder *encoder = &output->encoder;
> @@ -104,12 +104,11 @@ int vkms_output_init(struct vkms_device *vkmsdev, int index)
>   	}
>   
>   	if (vkmsdev->config->writeback) {
> -		writeback = vkms_enable_writeback_connector(vkmsdev);
> +		writeback = vkms_enable_writeback_connector(vkmsdev, index);
>   		if (writeback)
>   			DRM_ERROR("Failed to init writeback connector\n");
>   	}
>   
> -	drm_mode_config_reset(dev);
>   
>   	return 0;
>   
> diff --git a/drivers/gpu/drm/vkms/vkms_writeback.c b/drivers/gpu/drm/vkms/vkms_writeback.c
> index 84a51cd281b9..5f291689f870 100644
> --- a/drivers/gpu/drm/vkms/vkms_writeback.c
> +++ b/drivers/gpu/drm/vkms/vkms_writeback.c
> @@ -101,7 +101,7 @@ static void vkms_wb_cleanup_job(struct drm_writeback_connector *connector,
>   				struct drm_writeback_job *job)
>   {
>   	struct vkms_writeback_job *vkmsjob = job->priv;
> -	struct vkms_device *vkmsdev;
> +	struct vkms_output *output = wb_connector_to_vkms_output(connector);
>   
>   	if (!job->fb)
>   		return;
> @@ -110,19 +110,18 @@ static void vkms_wb_cleanup_job(struct drm_writeback_connector *connector,
>   
>   	drm_framebuffer_put(vkmsjob->wb_frame_info.fb);
>   
> -	vkmsdev = drm_device_to_vkms_device(job->fb->dev);
> -	vkms_set_composer(&vkmsdev->output, false);
> +	vkms_set_composer(output, false);
>   	kfree(vkmsjob);
>   }
>   
>   static void vkms_wb_atomic_commit(struct drm_connector *conn,
>   				  struct drm_atomic_state *state)
>   {
> -	struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
> -											 conn);
> -	struct vkms_device *vkmsdev = drm_device_to_vkms_device(conn->dev);
> -	struct vkms_output *output = &vkmsdev->output;
> -	struct drm_writeback_connector *wb_conn = &output->wb_connector;
> +	struct drm_connector_state *connector_state =
> +		drm_atomic_get_new_connector_state(state, conn);
> +	struct drm_writeback_job *wb_job = connector_state->writeback_job;
> +	struct drm_writeback_connector *wb_conn = wb_job->connector;
> +	struct vkms_output *output = wb_connector_to_vkms_output(wb_conn);
>   	struct drm_connector_state *conn_state = wb_conn->base.state;
>   	struct vkms_crtc_state *crtc_state = output->composer_state;
>   	struct drm_framebuffer *fb = connector_state->writeback_job->fb;
> @@ -135,7 +134,7 @@ static void vkms_wb_atomic_commit(struct drm_connector *conn,
>   	if (!conn_state)
>   		return;
>   
> -	vkms_set_composer(&vkmsdev->output, true);
> +	vkms_set_composer(output, true);
>   
>   	active_wb = conn_state->writeback_job->priv;
>   	wb_frame_info = &active_wb->wb_frame_info;
> @@ -147,6 +146,7 @@ static void vkms_wb_atomic_commit(struct drm_connector *conn,
>   	wb_frame_info->cpp = fb->format->cpp[0];
>   	crtc_state->wb_pending = true;
>   	spin_unlock_irq(&output->composer_lock);
> +
>   	drm_writeback_queue_job(wb_conn, connector_state);
>   	active_wb->wb_write = get_line_to_frame_function(wb_format);
>   	drm_rect_init(&wb_frame_info->src, 0, 0, crtc_width, crtc_height);
> @@ -160,9 +160,9 @@ static const struct drm_connector_helper_funcs vkms_wb_conn_helper_funcs = {
>   	.atomic_commit = vkms_wb_atomic_commit,
>   };
>   
> -int vkms_enable_writeback_connector(struct vkms_device *vkmsdev)
> +int vkms_enable_writeback_connector(struct vkms_device *vkmsdev, int index)
>   {
> -	struct drm_writeback_connector *wb = &vkmsdev->output.wb_connector;
> +	struct drm_writeback_connector *wb = &vkmsdev->output[index].wb_connector;
>   
>   	drm_connector_helper_add(&wb->base, &vkms_wb_conn_helper_funcs);
>   
> @@ -171,5 +171,5 @@ int vkms_enable_writeback_connector(struct vkms_device *vkmsdev)
>   					    &vkms_wb_encoder_helper_funcs,
>   					    vkms_wb_formats,
>   					    ARRAY_SIZE(vkms_wb_formats),
> -					    1);
> +					    1 << index);
>   }
Marius Vlad May 8, 2023, 8:01 a.m. UTC | #2
On 5/4/23 15:20, Maíra Canal wrote:
> Hi Marius,
> 
> I'm not sure if you'll keep working in this series after the proposal of
> the Chromium folks. Anyway, just a small comment.

Hi Maíra,

No worries, thanks for a lot for taking another look. I've added another 
patch to adjust the number of overalys, rather than the pipes. It will 
probably take some time until that configFS series make it through though.

> 
> On 4/26/23 07:40, Marius Vlad wrote:
>> This adds support for creating multiple virtual pipes, in case one would
>> need to display multiple independent things on different outputs.
>>
>> We use a module parameter to specify how many to create,  defaulting to
>> just one with the possibility of specifying a maximum of 4 possible 
>> pipes.
>>
>> This is of particular importance when testing out the DRM back-end in
>> compositors, but also to be able to independently set different DPMS 
>> states.
>>
>> Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
>> ---
>>   drivers/gpu/drm/vkms/vkms_crtc.c      |  3 +--
>>   drivers/gpu/drm/vkms/vkms_drv.c       | 38 ++++++++++++++++++++++-----
>>   drivers/gpu/drm/vkms/vkms_drv.h       | 12 ++++++---
>>   drivers/gpu/drm/vkms/vkms_output.c    |  5 ++--
>>   drivers/gpu/drm/vkms/vkms_writeback.c | 24 ++++++++---------
>>   5 files changed, 55 insertions(+), 27 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c 
>> b/drivers/gpu/drm/vkms/vkms_crtc.c
>> index 515f6772b866..051e7b647db9 100644
>> --- a/drivers/gpu/drm/vkms/vkms_crtc.c
>> +++ b/drivers/gpu/drm/vkms/vkms_crtc.c
>> @@ -89,8 +89,7 @@ static bool vkms_get_vblank_timestamp(struct 
>> drm_crtc *crtc,
>>   {
>>       struct drm_device *dev = crtc->dev;
>>       unsigned int pipe = crtc->index;
>> -    struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
>> -    struct vkms_output *output = &vkmsdev->output;
>> +    struct vkms_output *output = drm_crtc_to_vkms_output(crtc);
>>       struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
>>       if (!READ_ONCE(vblank->enabled)) {
>> diff --git a/drivers/gpu/drm/vkms/vkms_drv.c 
>> b/drivers/gpu/drm/vkms/vkms_drv.c
>> index e3c9c9571c8d..0ee17065bc6f 100644
>> --- a/drivers/gpu/drm/vkms/vkms_drv.c
>> +++ b/drivers/gpu/drm/vkms/vkms_drv.c
> 
> [...]
> 
>> @@ -230,7 +243,18 @@ static int __init vkms_init(void)
>>       config->cursor = enable_cursor;
>>       config->writeback = enable_writeback;
>>       config->overlay = enable_overlay;
>> -
>> +    config->max_pipes = max_pipes;
>> +    if (config->max_pipes > NUM_MAX_PIPES)
>> +        config->max_pipes = NUM_MAX_PIPES;
>> +    if (config->max_pipes < NUM_MIN_PIPES)
>> +        config->max_pipes = NUM_MIN_PIPES;
>> +
>> +    /* with 8 overlays and 4 pipes we exceed the amount of planes we
>> +     * have; see __drm_universal_plane_init() */
>> +    if (config->overlay && config->max_pipes == NUM_MAX_PIPES)
>> +        config->max_pipes = NUM_MAX_PIPES - 1;
> 
> Instead of reducing the number of pipes, you could change the maximum 
> number
> of overlay planes to 6-4 planes. So many overlay planes are not that needed
> and it would be nice to have 4 pipes with overlay planes enabled.
> 
> Best Regards,
> - Maíra Canal
> 
>> +
>> +    DRM_INFO("Initialized with %u pipes\n", config->max_pipes);
>>       ret = vkms_create(config);
>>       if (ret)
>>           kfree(config);
>> diff --git a/drivers/gpu/drm/vkms/vkms_drv.h 
>> b/drivers/gpu/drm/vkms/vkms_drv.h
>> index f152d54baf76..b096832ed0f2 100644
>> --- a/drivers/gpu/drm/vkms/vkms_drv.h
>> +++ b/drivers/gpu/drm/vkms/vkms_drv.h
>> @@ -21,7 +21,9 @@
>>   #define XRES_MAX  8192
>>   #define YRES_MAX  8192
>> -#define NUM_OVERLAY_PLANES 8
>> +#define NUM_OVERLAY_PLANES    8
>> +#define NUM_MAX_PIPES        4
>> +#define NUM_MIN_PIPES        1
>>   struct vkms_frame_info {
>>       struct drm_framebuffer *fb;
>> @@ -112,6 +114,7 @@ struct vkms_config {
>>       bool writeback;
>>       bool cursor;
>>       bool overlay;
>> +    unsigned int max_pipes;
>>       /* only set when instantiated */
>>       struct vkms_device *dev;
>>   };
>> @@ -119,13 +122,16 @@ struct vkms_config {
>>   struct vkms_device {
>>       struct drm_device drm;
>>       struct platform_device *platform;
>> -    struct vkms_output output;
>> +    struct vkms_output output[NUM_MAX_PIPES];
>>       const struct vkms_config *config;
>>   };
>>   #define drm_crtc_to_vkms_output(target) \
>>       container_of(target, struct vkms_output, crtc)
>> +#define wb_connector_to_vkms_output(target) \
>> +    container_of(target, struct vkms_output, wb_connector)
>> +
>>   #define drm_device_to_vkms_device(target) \
>>       container_of(target, struct vkms_device, drm)
>> @@ -157,6 +163,6 @@ void vkms_set_composer(struct vkms_output *out, 
>> bool enabled);
>>   void vkms_compose_row(struct line_buffer *stage_buffer, struct 
>> vkms_plane_state *plane, int y);
>>   /* Writeback */
>> -int vkms_enable_writeback_connector(struct vkms_device *vkmsdev);
>> +int vkms_enable_writeback_connector(struct vkms_device *vkmsdev, int 
>> index);
>>   #endif /* _VKMS_DRV_H_ */
>> diff --git a/drivers/gpu/drm/vkms/vkms_output.c 
>> b/drivers/gpu/drm/vkms/vkms_output.c
>> index 1b8aa3a34e46..9c801b8c4c12 100644
>> --- a/drivers/gpu/drm/vkms/vkms_output.c
>> +++ b/drivers/gpu/drm/vkms/vkms_output.c
>> @@ -48,7 +48,7 @@ static int vkms_add_overlay_plane(struct vkms_device 
>> *vkmsdev, int index,
>>   int vkms_output_init(struct vkms_device *vkmsdev, int index)
>>   {
>> -    struct vkms_output *output = &vkmsdev->output;
>> +    struct vkms_output *output = &vkmsdev->output[index];
>>       struct drm_device *dev = &vkmsdev->drm;
>>       struct drm_connector *connector = &output->connector;
>>       struct drm_encoder *encoder = &output->encoder;
>> @@ -104,12 +104,11 @@ int vkms_output_init(struct vkms_device 
>> *vkmsdev, int index)
>>       }
>>       if (vkmsdev->config->writeback) {
>> -        writeback = vkms_enable_writeback_connector(vkmsdev);
>> +        writeback = vkms_enable_writeback_connector(vkmsdev, index);
>>           if (writeback)
>>               DRM_ERROR("Failed to init writeback connector\n");
>>       }
>> -    drm_mode_config_reset(dev);
>>       return 0;
>> diff --git a/drivers/gpu/drm/vkms/vkms_writeback.c 
>> b/drivers/gpu/drm/vkms/vkms_writeback.c
>> index 84a51cd281b9..5f291689f870 100644
>> --- a/drivers/gpu/drm/vkms/vkms_writeback.c
>> +++ b/drivers/gpu/drm/vkms/vkms_writeback.c
>> @@ -101,7 +101,7 @@ static void vkms_wb_cleanup_job(struct 
>> drm_writeback_connector *connector,
>>                   struct drm_writeback_job *job)
>>   {
>>       struct vkms_writeback_job *vkmsjob = job->priv;
>> -    struct vkms_device *vkmsdev;
>> +    struct vkms_output *output = wb_connector_to_vkms_output(connector);
>>       if (!job->fb)
>>           return;
>> @@ -110,19 +110,18 @@ static void vkms_wb_cleanup_job(struct 
>> drm_writeback_connector *connector,
>>       drm_framebuffer_put(vkmsjob->wb_frame_info.fb);
>> -    vkmsdev = drm_device_to_vkms_device(job->fb->dev);
>> -    vkms_set_composer(&vkmsdev->output, false);
>> +    vkms_set_composer(output, false);
>>       kfree(vkmsjob);
>>   }
>>   static void vkms_wb_atomic_commit(struct drm_connector *conn,
>>                     struct drm_atomic_state *state)
>>   {
>> -    struct drm_connector_state *connector_state = 
>> drm_atomic_get_new_connector_state(state,
>> -                                             conn);
>> -    struct vkms_device *vkmsdev = drm_device_to_vkms_device(conn->dev);
>> -    struct vkms_output *output = &vkmsdev->output;
>> -    struct drm_writeback_connector *wb_conn = &output->wb_connector;
>> +    struct drm_connector_state *connector_state =
>> +        drm_atomic_get_new_connector_state(state, conn);
>> +    struct drm_writeback_job *wb_job = connector_state->writeback_job;
>> +    struct drm_writeback_connector *wb_conn = wb_job->connector;
>> +    struct vkms_output *output = wb_connector_to_vkms_output(wb_conn);
>>       struct drm_connector_state *conn_state = wb_conn->base.state;
>>       struct vkms_crtc_state *crtc_state = output->composer_state;
>>       struct drm_framebuffer *fb = connector_state->writeback_job->fb;
>> @@ -135,7 +134,7 @@ static void vkms_wb_atomic_commit(struct 
>> drm_connector *conn,
>>       if (!conn_state)
>>           return;
>> -    vkms_set_composer(&vkmsdev->output, true);
>> +    vkms_set_composer(output, true);
>>       active_wb = conn_state->writeback_job->priv;
>>       wb_frame_info = &active_wb->wb_frame_info;
>> @@ -147,6 +146,7 @@ static void vkms_wb_atomic_commit(struct 
>> drm_connector *conn,
>>       wb_frame_info->cpp = fb->format->cpp[0];
>>       crtc_state->wb_pending = true;
>>       spin_unlock_irq(&output->composer_lock);
>> +
>>       drm_writeback_queue_job(wb_conn, connector_state);
>>       active_wb->wb_write = get_line_to_frame_function(wb_format);
>>       drm_rect_init(&wb_frame_info->src, 0, 0, crtc_width, crtc_height);
>> @@ -160,9 +160,9 @@ static const struct drm_connector_helper_funcs 
>> vkms_wb_conn_helper_funcs = {
>>       .atomic_commit = vkms_wb_atomic_commit,
>>   };
>> -int vkms_enable_writeback_connector(struct vkms_device *vkmsdev)
>> +int vkms_enable_writeback_connector(struct vkms_device *vkmsdev, int 
>> index)
>>   {
>> -    struct drm_writeback_connector *wb = &vkmsdev->output.wb_connector;
>> +    struct drm_writeback_connector *wb = 
>> &vkmsdev->output[index].wb_connector;
>>       drm_connector_helper_add(&wb->base, &vkms_wb_conn_helper_funcs);
>> @@ -171,5 +171,5 @@ int vkms_enable_writeback_connector(struct 
>> vkms_device *vkmsdev)
>>                           &vkms_wb_encoder_helper_funcs,
>>                           vkms_wb_formats,
>>                           ARRAY_SIZE(vkms_wb_formats),
>> -                        1);
>> +                        1 << index);
>>   }
diff mbox series

Patch

diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
index 515f6772b866..051e7b647db9 100644
--- a/drivers/gpu/drm/vkms/vkms_crtc.c
+++ b/drivers/gpu/drm/vkms/vkms_crtc.c
@@ -89,8 +89,7 @@  static bool vkms_get_vblank_timestamp(struct drm_crtc *crtc,
 {
 	struct drm_device *dev = crtc->dev;
 	unsigned int pipe = crtc->index;
-	struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
-	struct vkms_output *output = &vkmsdev->output;
+	struct vkms_output *output = drm_crtc_to_vkms_output(crtc);
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 
 	if (!READ_ONCE(vblank->enabled)) {
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index e3c9c9571c8d..0ee17065bc6f 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -51,14 +51,19 @@  static bool enable_overlay;
 module_param_named(enable_overlay, enable_overlay, bool, 0444);
 MODULE_PARM_DESC(enable_overlay, "Enable/Disable overlay support");
 
+static unsigned int max_pipes = 1;
+module_param_named(max_pipes, max_pipes, int, 0444);
+MODULE_PARM_DESC(max_pipes, "Specify how many pipes to create");
+
 DEFINE_DRM_GEM_FOPS(vkms_driver_fops);
 
 static void vkms_release(struct drm_device *dev)
 {
-	struct vkms_device *vkms = drm_device_to_vkms_device(dev);
+	int i;
+	struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
 
-	if (vkms->output.composer_workq)
-		destroy_workqueue(vkms->output.composer_workq);
+	for (i = 0; i < vkmsdev->config->max_pipes; i++)
+		destroy_workqueue(vkmsdev->output[i].composer_workq);
 }
 
 static void vkms_atomic_commit_tail(struct drm_atomic_state *old_state)
@@ -99,6 +104,7 @@  static int vkms_config_show(struct seq_file *m, void *data)
 	seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback);
 	seq_printf(m, "cursor=%d\n", vkmsdev->config->cursor);
 	seq_printf(m, "overlay=%d\n", vkmsdev->config->overlay);
+	seq_printf(m, "pipes=%d\n", vkmsdev->config->max_pipes);
 
 	return 0;
 }
@@ -133,7 +139,7 @@  static const struct drm_mode_config_helper_funcs vkms_mode_config_helpers = {
 static int vkms_modeset_init(struct vkms_device *vkmsdev)
 {
 	struct drm_device *dev = &vkmsdev->drm;
-	int ret;
+	int ret, i;
 
 	ret = drmm_mode_config_init(dev);
 	if (ret)
@@ -152,7 +158,14 @@  static int vkms_modeset_init(struct vkms_device *vkmsdev)
 	dev->mode_config.preferred_depth = 0;
 	dev->mode_config.helper_private = &vkms_mode_config_helpers;
 
-	return vkms_output_init(vkmsdev, 0);
+	for (i = 0; i < vkmsdev->config->max_pipes; i++) {
+		ret = vkms_output_init(vkmsdev, i);
+		if (ret)
+			return ret;
+	}
+
+	drm_mode_config_reset(dev);
+	return ret;
 }
 
 static int vkms_create(struct vkms_config *config)
@@ -188,7 +201,7 @@  static int vkms_create(struct vkms_config *config)
 		goto out_devres;
 	}
 
-	ret = drm_vblank_init(&vkms_device->drm, 1);
+	ret = drm_vblank_init(&vkms_device->drm, config->max_pipes);
 	if (ret) {
 		DRM_ERROR("Failed to vblank\n");
 		goto out_devres;
@@ -230,7 +243,18 @@  static int __init vkms_init(void)
 	config->cursor = enable_cursor;
 	config->writeback = enable_writeback;
 	config->overlay = enable_overlay;
-
+	config->max_pipes = max_pipes;
+	if (config->max_pipes > NUM_MAX_PIPES)
+		config->max_pipes = NUM_MAX_PIPES;
+	if (config->max_pipes < NUM_MIN_PIPES)
+		config->max_pipes = NUM_MIN_PIPES;
+
+	/* with 8 overlays and 4 pipes we exceed the amount of planes we
+	 * have; see __drm_universal_plane_init() */
+	if (config->overlay && config->max_pipes == NUM_MAX_PIPES)
+		config->max_pipes = NUM_MAX_PIPES - 1;
+
+	DRM_INFO("Initialized with %u pipes\n", config->max_pipes);
 	ret = vkms_create(config);
 	if (ret)
 		kfree(config);
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index f152d54baf76..b096832ed0f2 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -21,7 +21,9 @@ 
 #define XRES_MAX  8192
 #define YRES_MAX  8192
 
-#define NUM_OVERLAY_PLANES 8
+#define NUM_OVERLAY_PLANES	8
+#define NUM_MAX_PIPES		4
+#define NUM_MIN_PIPES		1
 
 struct vkms_frame_info {
 	struct drm_framebuffer *fb;
@@ -112,6 +114,7 @@  struct vkms_config {
 	bool writeback;
 	bool cursor;
 	bool overlay;
+	unsigned int max_pipes;
 	/* only set when instantiated */
 	struct vkms_device *dev;
 };
@@ -119,13 +122,16 @@  struct vkms_config {
 struct vkms_device {
 	struct drm_device drm;
 	struct platform_device *platform;
-	struct vkms_output output;
+	struct vkms_output output[NUM_MAX_PIPES];
 	const struct vkms_config *config;
 };
 
 #define drm_crtc_to_vkms_output(target) \
 	container_of(target, struct vkms_output, crtc)
 
+#define wb_connector_to_vkms_output(target) \
+	container_of(target, struct vkms_output, wb_connector)
+
 #define drm_device_to_vkms_device(target) \
 	container_of(target, struct vkms_device, drm)
 
@@ -157,6 +163,6 @@  void vkms_set_composer(struct vkms_output *out, bool enabled);
 void vkms_compose_row(struct line_buffer *stage_buffer, struct vkms_plane_state *plane, int y);
 
 /* Writeback */
-int vkms_enable_writeback_connector(struct vkms_device *vkmsdev);
+int vkms_enable_writeback_connector(struct vkms_device *vkmsdev, int index);
 
 #endif /* _VKMS_DRV_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index 1b8aa3a34e46..9c801b8c4c12 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -48,7 +48,7 @@  static int vkms_add_overlay_plane(struct vkms_device *vkmsdev, int index,
 
 int vkms_output_init(struct vkms_device *vkmsdev, int index)
 {
-	struct vkms_output *output = &vkmsdev->output;
+	struct vkms_output *output = &vkmsdev->output[index];
 	struct drm_device *dev = &vkmsdev->drm;
 	struct drm_connector *connector = &output->connector;
 	struct drm_encoder *encoder = &output->encoder;
@@ -104,12 +104,11 @@  int vkms_output_init(struct vkms_device *vkmsdev, int index)
 	}
 
 	if (vkmsdev->config->writeback) {
-		writeback = vkms_enable_writeback_connector(vkmsdev);
+		writeback = vkms_enable_writeback_connector(vkmsdev, index);
 		if (writeback)
 			DRM_ERROR("Failed to init writeback connector\n");
 	}
 
-	drm_mode_config_reset(dev);
 
 	return 0;
 
diff --git a/drivers/gpu/drm/vkms/vkms_writeback.c b/drivers/gpu/drm/vkms/vkms_writeback.c
index 84a51cd281b9..5f291689f870 100644
--- a/drivers/gpu/drm/vkms/vkms_writeback.c
+++ b/drivers/gpu/drm/vkms/vkms_writeback.c
@@ -101,7 +101,7 @@  static void vkms_wb_cleanup_job(struct drm_writeback_connector *connector,
 				struct drm_writeback_job *job)
 {
 	struct vkms_writeback_job *vkmsjob = job->priv;
-	struct vkms_device *vkmsdev;
+	struct vkms_output *output = wb_connector_to_vkms_output(connector);
 
 	if (!job->fb)
 		return;
@@ -110,19 +110,18 @@  static void vkms_wb_cleanup_job(struct drm_writeback_connector *connector,
 
 	drm_framebuffer_put(vkmsjob->wb_frame_info.fb);
 
-	vkmsdev = drm_device_to_vkms_device(job->fb->dev);
-	vkms_set_composer(&vkmsdev->output, false);
+	vkms_set_composer(output, false);
 	kfree(vkmsjob);
 }
 
 static void vkms_wb_atomic_commit(struct drm_connector *conn,
 				  struct drm_atomic_state *state)
 {
-	struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
-											 conn);
-	struct vkms_device *vkmsdev = drm_device_to_vkms_device(conn->dev);
-	struct vkms_output *output = &vkmsdev->output;
-	struct drm_writeback_connector *wb_conn = &output->wb_connector;
+	struct drm_connector_state *connector_state =
+		drm_atomic_get_new_connector_state(state, conn);
+	struct drm_writeback_job *wb_job = connector_state->writeback_job;
+	struct drm_writeback_connector *wb_conn = wb_job->connector;
+	struct vkms_output *output = wb_connector_to_vkms_output(wb_conn);
 	struct drm_connector_state *conn_state = wb_conn->base.state;
 	struct vkms_crtc_state *crtc_state = output->composer_state;
 	struct drm_framebuffer *fb = connector_state->writeback_job->fb;
@@ -135,7 +134,7 @@  static void vkms_wb_atomic_commit(struct drm_connector *conn,
 	if (!conn_state)
 		return;
 
-	vkms_set_composer(&vkmsdev->output, true);
+	vkms_set_composer(output, true);
 
 	active_wb = conn_state->writeback_job->priv;
 	wb_frame_info = &active_wb->wb_frame_info;
@@ -147,6 +146,7 @@  static void vkms_wb_atomic_commit(struct drm_connector *conn,
 	wb_frame_info->cpp = fb->format->cpp[0];
 	crtc_state->wb_pending = true;
 	spin_unlock_irq(&output->composer_lock);
+
 	drm_writeback_queue_job(wb_conn, connector_state);
 	active_wb->wb_write = get_line_to_frame_function(wb_format);
 	drm_rect_init(&wb_frame_info->src, 0, 0, crtc_width, crtc_height);
@@ -160,9 +160,9 @@  static const struct drm_connector_helper_funcs vkms_wb_conn_helper_funcs = {
 	.atomic_commit = vkms_wb_atomic_commit,
 };
 
-int vkms_enable_writeback_connector(struct vkms_device *vkmsdev)
+int vkms_enable_writeback_connector(struct vkms_device *vkmsdev, int index)
 {
-	struct drm_writeback_connector *wb = &vkmsdev->output.wb_connector;
+	struct drm_writeback_connector *wb = &vkmsdev->output[index].wb_connector;
 
 	drm_connector_helper_add(&wb->base, &vkms_wb_conn_helper_funcs);
 
@@ -171,5 +171,5 @@  int vkms_enable_writeback_connector(struct vkms_device *vkmsdev)
 					    &vkms_wb_encoder_helper_funcs,
 					    vkms_wb_formats,
 					    ARRAY_SIZE(vkms_wb_formats),
-					    1);
+					    1 << index);
 }