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