xref: /openbmc/linux/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
176c56a5aSDeepak Rawat // SPDX-License-Identifier: GPL-2.0-only
276c56a5aSDeepak Rawat /*
376c56a5aSDeepak Rawat  * Copyright 2021 Microsoft
476c56a5aSDeepak Rawat  */
576c56a5aSDeepak Rawat 
676c56a5aSDeepak Rawat #include <linux/hyperv.h>
776c56a5aSDeepak Rawat 
876c56a5aSDeepak Rawat #include <drm/drm_damage_helper.h>
976c56a5aSDeepak Rawat #include <drm/drm_drv.h>
10009a3a52SThomas Zimmermann #include <drm/drm_edid.h>
1176c56a5aSDeepak Rawat #include <drm/drm_format_helper.h>
1276c56a5aSDeepak Rawat #include <drm/drm_fourcc.h>
13009a3a52SThomas Zimmermann #include <drm/drm_framebuffer.h>
1476c56a5aSDeepak Rawat #include <drm/drm_gem_atomic_helper.h>
1576c56a5aSDeepak Rawat #include <drm/drm_gem_framebuffer_helper.h>
1676c56a5aSDeepak Rawat #include <drm/drm_gem_shmem_helper.h>
1776c56a5aSDeepak Rawat #include <drm/drm_probe_helper.h>
1876c56a5aSDeepak Rawat #include <drm/drm_simple_kms_helper.h>
1976c56a5aSDeepak Rawat 
2076c56a5aSDeepak Rawat #include "hyperv_drm.h"
2176c56a5aSDeepak Rawat 
hyperv_blit_to_vram_rect(struct drm_framebuffer * fb,const struct iosys_map * vmap,struct drm_rect * rect)2276c56a5aSDeepak Rawat static int hyperv_blit_to_vram_rect(struct drm_framebuffer *fb,
23*edbe262aSThomas Zimmermann 				    const struct iosys_map *vmap,
2476c56a5aSDeepak Rawat 				    struct drm_rect *rect)
2576c56a5aSDeepak Rawat {
2676c56a5aSDeepak Rawat 	struct hyperv_drm_device *hv = to_hv(fb->dev);
27*edbe262aSThomas Zimmermann 	struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(hv->vram);
2876c56a5aSDeepak Rawat 	int idx;
2976c56a5aSDeepak Rawat 
3076c56a5aSDeepak Rawat 	if (!drm_dev_enter(&hv->dev, &idx))
3176c56a5aSDeepak Rawat 		return -ENODEV;
3276c56a5aSDeepak Rawat 
33*edbe262aSThomas Zimmermann 	iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, rect));
34*edbe262aSThomas Zimmermann 	drm_fb_memcpy(&dst, fb->pitches, vmap, fb, rect);
3527bd66ddSThomas Zimmermann 
3676c56a5aSDeepak Rawat 	drm_dev_exit(idx);
3776c56a5aSDeepak Rawat 
3876c56a5aSDeepak Rawat 	return 0;
3976c56a5aSDeepak Rawat }
4076c56a5aSDeepak Rawat 
hyperv_blit_to_vram_fullscreen(struct drm_framebuffer * fb,const struct iosys_map * map)417938f421SLucas De Marchi static int hyperv_blit_to_vram_fullscreen(struct drm_framebuffer *fb,
427938f421SLucas De Marchi 					  const struct iosys_map *map)
4376c56a5aSDeepak Rawat {
4476c56a5aSDeepak Rawat 	struct drm_rect fullscreen = {
4576c56a5aSDeepak Rawat 		.x1 = 0,
4676c56a5aSDeepak Rawat 		.x2 = fb->width,
4776c56a5aSDeepak Rawat 		.y1 = 0,
4876c56a5aSDeepak Rawat 		.y2 = fb->height,
4976c56a5aSDeepak Rawat 	};
5076c56a5aSDeepak Rawat 	return hyperv_blit_to_vram_rect(fb, map, &fullscreen);
5176c56a5aSDeepak Rawat }
5276c56a5aSDeepak Rawat 
hyperv_connector_get_modes(struct drm_connector * connector)5376c56a5aSDeepak Rawat static int hyperv_connector_get_modes(struct drm_connector *connector)
5476c56a5aSDeepak Rawat {
5576c56a5aSDeepak Rawat 	struct hyperv_drm_device *hv = to_hv(connector->dev);
5676c56a5aSDeepak Rawat 	int count;
5776c56a5aSDeepak Rawat 
5876c56a5aSDeepak Rawat 	count = drm_add_modes_noedid(connector,
5976c56a5aSDeepak Rawat 				     connector->dev->mode_config.max_width,
6076c56a5aSDeepak Rawat 				     connector->dev->mode_config.max_height);
6176c56a5aSDeepak Rawat 	drm_set_preferred_mode(connector, hv->preferred_width,
6276c56a5aSDeepak Rawat 			       hv->preferred_height);
6376c56a5aSDeepak Rawat 
6476c56a5aSDeepak Rawat 	return count;
6576c56a5aSDeepak Rawat }
6676c56a5aSDeepak Rawat 
6776c56a5aSDeepak Rawat static const struct drm_connector_helper_funcs hyperv_connector_helper_funcs = {
6876c56a5aSDeepak Rawat 	.get_modes = hyperv_connector_get_modes,
6976c56a5aSDeepak Rawat };
7076c56a5aSDeepak Rawat 
7176c56a5aSDeepak Rawat static const struct drm_connector_funcs hyperv_connector_funcs = {
7276c56a5aSDeepak Rawat 	.fill_modes = drm_helper_probe_single_connector_modes,
7376c56a5aSDeepak Rawat 	.destroy = drm_connector_cleanup,
7476c56a5aSDeepak Rawat 	.reset = drm_atomic_helper_connector_reset,
7576c56a5aSDeepak Rawat 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
7676c56a5aSDeepak Rawat 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
7776c56a5aSDeepak Rawat };
7876c56a5aSDeepak Rawat 
hyperv_conn_init(struct hyperv_drm_device * hv)7976c56a5aSDeepak Rawat static inline int hyperv_conn_init(struct hyperv_drm_device *hv)
8076c56a5aSDeepak Rawat {
8176c56a5aSDeepak Rawat 	drm_connector_helper_add(&hv->connector, &hyperv_connector_helper_funcs);
8276c56a5aSDeepak Rawat 	return drm_connector_init(&hv->dev, &hv->connector,
8376c56a5aSDeepak Rawat 				  &hyperv_connector_funcs,
8476c56a5aSDeepak Rawat 				  DRM_MODE_CONNECTOR_VIRTUAL);
8576c56a5aSDeepak Rawat }
8676c56a5aSDeepak Rawat 
hyperv_check_size(struct hyperv_drm_device * hv,int w,int h,struct drm_framebuffer * fb)8776c56a5aSDeepak Rawat static int hyperv_check_size(struct hyperv_drm_device *hv, int w, int h,
8876c56a5aSDeepak Rawat 			     struct drm_framebuffer *fb)
8976c56a5aSDeepak Rawat {
9076c56a5aSDeepak Rawat 	u32 pitch = w * (hv->screen_depth / 8);
9176c56a5aSDeepak Rawat 
9276c56a5aSDeepak Rawat 	if (fb)
9376c56a5aSDeepak Rawat 		pitch = fb->pitches[0];
9476c56a5aSDeepak Rawat 
9576c56a5aSDeepak Rawat 	if (pitch * h > hv->fb_size)
9676c56a5aSDeepak Rawat 		return -EINVAL;
9776c56a5aSDeepak Rawat 
9876c56a5aSDeepak Rawat 	return 0;
9976c56a5aSDeepak Rawat }
10076c56a5aSDeepak Rawat 
hyperv_pipe_enable(struct drm_simple_display_pipe * pipe,struct drm_crtc_state * crtc_state,struct drm_plane_state * plane_state)10176c56a5aSDeepak Rawat static void hyperv_pipe_enable(struct drm_simple_display_pipe *pipe,
10276c56a5aSDeepak Rawat 			       struct drm_crtc_state *crtc_state,
10376c56a5aSDeepak Rawat 			       struct drm_plane_state *plane_state)
10476c56a5aSDeepak Rawat {
10576c56a5aSDeepak Rawat 	struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev);
10676c56a5aSDeepak Rawat 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
10776c56a5aSDeepak Rawat 
108b253c302SDexuan Cui 	hyperv_hide_hw_ptr(hv->hdev);
10976c56a5aSDeepak Rawat 	hyperv_update_situation(hv->hdev, 1,  hv->screen_depth,
11076c56a5aSDeepak Rawat 				crtc_state->mode.hdisplay,
11176c56a5aSDeepak Rawat 				crtc_state->mode.vdisplay,
11276c56a5aSDeepak Rawat 				plane_state->fb->pitches[0]);
11370594e8bSThomas Zimmermann 	hyperv_blit_to_vram_fullscreen(plane_state->fb, &shadow_plane_state->data[0]);
11476c56a5aSDeepak Rawat }
11576c56a5aSDeepak Rawat 
hyperv_pipe_check(struct drm_simple_display_pipe * pipe,struct drm_plane_state * plane_state,struct drm_crtc_state * crtc_state)11676c56a5aSDeepak Rawat static int hyperv_pipe_check(struct drm_simple_display_pipe *pipe,
11776c56a5aSDeepak Rawat 			     struct drm_plane_state *plane_state,
11876c56a5aSDeepak Rawat 			     struct drm_crtc_state *crtc_state)
11976c56a5aSDeepak Rawat {
12076c56a5aSDeepak Rawat 	struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev);
12176c56a5aSDeepak Rawat 	struct drm_framebuffer *fb = plane_state->fb;
12276c56a5aSDeepak Rawat 
12376c56a5aSDeepak Rawat 	if (fb->format->format != DRM_FORMAT_XRGB8888)
12476c56a5aSDeepak Rawat 		return -EINVAL;
12576c56a5aSDeepak Rawat 
1266733dd4aSSaurabh Sengar 	if (fb->pitches[0] * fb->height > hv->fb_size) {
1276733dd4aSSaurabh Sengar 		drm_err(&hv->dev, "fb size requested by %s for %dX%d (pitch %d) greater than %ld\n",
1286733dd4aSSaurabh Sengar 			current->comm, fb->width, fb->height, fb->pitches[0], hv->fb_size);
12976c56a5aSDeepak Rawat 		return -EINVAL;
1306733dd4aSSaurabh Sengar 	}
13176c56a5aSDeepak Rawat 
13276c56a5aSDeepak Rawat 	return 0;
13376c56a5aSDeepak Rawat }
13476c56a5aSDeepak Rawat 
hyperv_pipe_update(struct drm_simple_display_pipe * pipe,struct drm_plane_state * old_state)13576c56a5aSDeepak Rawat static void hyperv_pipe_update(struct drm_simple_display_pipe *pipe,
13676c56a5aSDeepak Rawat 			       struct drm_plane_state *old_state)
13776c56a5aSDeepak Rawat {
13876c56a5aSDeepak Rawat 	struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev);
13976c56a5aSDeepak Rawat 	struct drm_plane_state *state = pipe->plane.state;
14076c56a5aSDeepak Rawat 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
14176c56a5aSDeepak Rawat 	struct drm_rect rect;
14276c56a5aSDeepak Rawat 
14376c56a5aSDeepak Rawat 	if (drm_atomic_helper_damage_merged(old_state, state, &rect)) {
14470594e8bSThomas Zimmermann 		hyperv_blit_to_vram_rect(state->fb, &shadow_plane_state->data[0], &rect);
14576c56a5aSDeepak Rawat 		hyperv_update_dirt(hv->hdev, &rect);
14676c56a5aSDeepak Rawat 	}
14776c56a5aSDeepak Rawat }
14876c56a5aSDeepak Rawat 
14976c56a5aSDeepak Rawat static const struct drm_simple_display_pipe_funcs hyperv_pipe_funcs = {
15076c56a5aSDeepak Rawat 	.enable	= hyperv_pipe_enable,
15176c56a5aSDeepak Rawat 	.check = hyperv_pipe_check,
15276c56a5aSDeepak Rawat 	.update	= hyperv_pipe_update,
15376c56a5aSDeepak Rawat 	DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
15476c56a5aSDeepak Rawat };
15576c56a5aSDeepak Rawat 
15676c56a5aSDeepak Rawat static const uint32_t hyperv_formats[] = {
15776c56a5aSDeepak Rawat 	DRM_FORMAT_XRGB8888,
15876c56a5aSDeepak Rawat };
15976c56a5aSDeepak Rawat 
16076c56a5aSDeepak Rawat static const uint64_t hyperv_modifiers[] = {
16176c56a5aSDeepak Rawat 	DRM_FORMAT_MOD_LINEAR,
16276c56a5aSDeepak Rawat 	DRM_FORMAT_MOD_INVALID
16376c56a5aSDeepak Rawat };
16476c56a5aSDeepak Rawat 
hyperv_pipe_init(struct hyperv_drm_device * hv)16576c56a5aSDeepak Rawat static inline int hyperv_pipe_init(struct hyperv_drm_device *hv)
16676c56a5aSDeepak Rawat {
16776c56a5aSDeepak Rawat 	int ret;
16876c56a5aSDeepak Rawat 
16976c56a5aSDeepak Rawat 	ret = drm_simple_display_pipe_init(&hv->dev,
17076c56a5aSDeepak Rawat 					   &hv->pipe,
17176c56a5aSDeepak Rawat 					   &hyperv_pipe_funcs,
17276c56a5aSDeepak Rawat 					   hyperv_formats,
17376c56a5aSDeepak Rawat 					   ARRAY_SIZE(hyperv_formats),
174d29b4295SPu Lehui 					   hyperv_modifiers,
17576c56a5aSDeepak Rawat 					   &hv->connector);
17676c56a5aSDeepak Rawat 	if (ret)
17776c56a5aSDeepak Rawat 		return ret;
17876c56a5aSDeepak Rawat 
17976c56a5aSDeepak Rawat 	drm_plane_enable_fb_damage_clips(&hv->pipe.plane);
18076c56a5aSDeepak Rawat 
18176c56a5aSDeepak Rawat 	return 0;
18276c56a5aSDeepak Rawat }
18376c56a5aSDeepak Rawat 
18476c56a5aSDeepak Rawat static enum drm_mode_status
hyperv_mode_valid(struct drm_device * dev,const struct drm_display_mode * mode)18576c56a5aSDeepak Rawat hyperv_mode_valid(struct drm_device *dev,
18676c56a5aSDeepak Rawat 		  const struct drm_display_mode *mode)
18776c56a5aSDeepak Rawat {
18876c56a5aSDeepak Rawat 	struct hyperv_drm_device *hv = to_hv(dev);
18976c56a5aSDeepak Rawat 
19076c56a5aSDeepak Rawat 	if (hyperv_check_size(hv, mode->hdisplay, mode->vdisplay, NULL))
19176c56a5aSDeepak Rawat 		return MODE_BAD;
19276c56a5aSDeepak Rawat 
19376c56a5aSDeepak Rawat 	return MODE_OK;
19476c56a5aSDeepak Rawat }
19576c56a5aSDeepak Rawat 
19676c56a5aSDeepak Rawat static const struct drm_mode_config_funcs hyperv_mode_config_funcs = {
19776c56a5aSDeepak Rawat 	.fb_create = drm_gem_fb_create_with_dirty,
19876c56a5aSDeepak Rawat 	.mode_valid = hyperv_mode_valid,
19976c56a5aSDeepak Rawat 	.atomic_check = drm_atomic_helper_check,
20076c56a5aSDeepak Rawat 	.atomic_commit = drm_atomic_helper_commit,
20176c56a5aSDeepak Rawat };
20276c56a5aSDeepak Rawat 
hyperv_mode_config_init(struct hyperv_drm_device * hv)20376c56a5aSDeepak Rawat int hyperv_mode_config_init(struct hyperv_drm_device *hv)
20476c56a5aSDeepak Rawat {
20576c56a5aSDeepak Rawat 	struct drm_device *dev = &hv->dev;
20676c56a5aSDeepak Rawat 	int ret;
20776c56a5aSDeepak Rawat 
20876c56a5aSDeepak Rawat 	ret = drmm_mode_config_init(dev);
20976c56a5aSDeepak Rawat 	if (ret) {
21076c56a5aSDeepak Rawat 		drm_err(dev, "Failed to initialized mode setting.\n");
21176c56a5aSDeepak Rawat 		return ret;
21276c56a5aSDeepak Rawat 	}
21376c56a5aSDeepak Rawat 
21476c56a5aSDeepak Rawat 	dev->mode_config.min_width = 0;
21576c56a5aSDeepak Rawat 	dev->mode_config.min_height = 0;
21676c56a5aSDeepak Rawat 	dev->mode_config.max_width = hv->screen_width_max;
21776c56a5aSDeepak Rawat 	dev->mode_config.max_height = hv->screen_height_max;
21876c56a5aSDeepak Rawat 
21976c56a5aSDeepak Rawat 	dev->mode_config.preferred_depth = hv->screen_depth;
22076c56a5aSDeepak Rawat 	dev->mode_config.prefer_shadow = 0;
22176c56a5aSDeepak Rawat 
22276c56a5aSDeepak Rawat 	dev->mode_config.funcs = &hyperv_mode_config_funcs;
22376c56a5aSDeepak Rawat 
22476c56a5aSDeepak Rawat 	ret = hyperv_conn_init(hv);
22576c56a5aSDeepak Rawat 	if (ret) {
22676c56a5aSDeepak Rawat 		drm_err(dev, "Failed to initialized connector.\n");
22776c56a5aSDeepak Rawat 		return ret;
22876c56a5aSDeepak Rawat 	}
22976c56a5aSDeepak Rawat 
23076c56a5aSDeepak Rawat 	ret = hyperv_pipe_init(hv);
23176c56a5aSDeepak Rawat 	if (ret) {
23276c56a5aSDeepak Rawat 		drm_err(dev, "Failed to initialized pipe.\n");
23376c56a5aSDeepak Rawat 		return ret;
23476c56a5aSDeepak Rawat 	}
23576c56a5aSDeepak Rawat 
23676c56a5aSDeepak Rawat 	drm_mode_config_reset(dev);
23776c56a5aSDeepak Rawat 
23876c56a5aSDeepak Rawat 	return 0;
23976c56a5aSDeepak Rawat }
240