aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/hyperv_fb.c
diff options
context:
space:
mode:
authorHaiyang Zhang <haiyangz@microsoft.com>2013-04-29 18:05:42 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-04-29 18:54:26 -0400
commit68a2d20b79b105f02dcbc52c211d7e62f98996b7 (patch)
tree7068bd5a37c0d85e92ff20ed079df1e74b676c61 /drivers/video/hyperv_fb.c
parent6cd472d3d2a9cace26eae924fe95ec21dc4a0502 (diff)
drivers/video: add Hyper-V Synthetic Video Frame Buffer Driver
This is the driver for the Hyper-V Synthetic Video, which supports screen resolution up to Full HD 1920x1080 on Windows Server 2012 host, and 1600x1200 on Windows Server 2008 R2 or earlier. It also solves the double mouse cursor issue of the emulated video mode. Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com> Reviewed-by: K. Y. Srinivasan <kys@microsoft.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Cc: Olaf Hering <olaf@aepfle.de> Cc: Geert Uytterhoeven <geert@linux-m68k.org> Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/hyperv_fb.c')
-rw-r--r--drivers/video/hyperv_fb.c829
1 files changed, 829 insertions, 0 deletions
diff --git a/drivers/video/hyperv_fb.c b/drivers/video/hyperv_fb.c
new file mode 100644
index 000000000000..d4d2c5fe2488
--- /dev/null
+++ b/drivers/video/hyperv_fb.c
@@ -0,0 +1,829 @@
1/*
2 * Copyright (c) 2012, Microsoft Corporation.
3 *
4 * Author:
5 * Haiyang Zhang <haiyangz@microsoft.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published
9 * by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
14 * NON INFRINGEMENT. See the GNU General Public License for more
15 * details.
16 */
17
18/*
19 * Hyper-V Synthetic Video Frame Buffer Driver
20 *
21 * This is the driver for the Hyper-V Synthetic Video, which supports
22 * screen resolution up to Full HD 1920x1080 with 32 bit color on Windows
23 * Server 2012, and 1600x1200 with 16 bit color on Windows Server 2008 R2
24 * or earlier.
25 *
26 * It also solves the double mouse cursor issue of the emulated video mode.
27 *
28 * The default screen resolution is 1152x864, which may be changed by a
29 * kernel parameter:
30 * video=hyperv_fb:<width>x<height>
31 * For example: video=hyperv_fb:1280x1024
32 *
33 * Portrait orientation is also supported:
34 * For example: video=hyperv_fb:864x1152
35 */
36
37#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
38
39#include <linux/module.h>
40#include <linux/kernel.h>
41#include <linux/init.h>
42#include <linux/completion.h>
43#include <linux/fb.h>
44#include <linux/pci.h>
45
46#include <linux/hyperv.h>
47
48
49/* Hyper-V Synthetic Video Protocol definitions and structures */
50#define MAX_VMBUS_PKT_SIZE 0x4000
51
52#define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major))
53#define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0)
54#define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2)
55
56#define SYNTHVID_DEPTH_WIN7 16
57#define SYNTHVID_DEPTH_WIN8 32
58
59#define SYNTHVID_FB_SIZE_WIN7 (4 * 1024 * 1024)
60#define SYNTHVID_WIDTH_MAX_WIN7 1600
61#define SYNTHVID_HEIGHT_MAX_WIN7 1200
62
63#define SYNTHVID_FB_SIZE_WIN8 (8 * 1024 * 1024)
64
65#define PCI_VENDOR_ID_MICROSOFT 0x1414
66#define PCI_DEVICE_ID_HYPERV_VIDEO 0x5353
67
68
69enum pipe_msg_type {
70 PIPE_MSG_INVALID,
71 PIPE_MSG_DATA,
72 PIPE_MSG_MAX
73};
74
75struct pipe_msg_hdr {
76 u32 type;
77 u32 size; /* size of message after this field */
78} __packed;
79
80
81enum synthvid_msg_type {
82 SYNTHVID_ERROR = 0,
83 SYNTHVID_VERSION_REQUEST = 1,
84 SYNTHVID_VERSION_RESPONSE = 2,
85 SYNTHVID_VRAM_LOCATION = 3,
86 SYNTHVID_VRAM_LOCATION_ACK = 4,
87 SYNTHVID_SITUATION_UPDATE = 5,
88 SYNTHVID_SITUATION_UPDATE_ACK = 6,
89 SYNTHVID_POINTER_POSITION = 7,
90 SYNTHVID_POINTER_SHAPE = 8,
91 SYNTHVID_FEATURE_CHANGE = 9,
92 SYNTHVID_DIRT = 10,
93
94 SYNTHVID_MAX = 11
95};
96
97struct synthvid_msg_hdr {
98 u32 type;
99 u32 size; /* size of this header + payload after this field*/
100} __packed;
101
102
103struct synthvid_version_req {
104 u32 version;
105} __packed;
106
107struct synthvid_version_resp {
108 u32 version;
109 u8 is_accepted;
110 u8 max_video_outputs;
111} __packed;
112
113struct synthvid_vram_location {
114 u64 user_ctx;
115 u8 is_vram_gpa_specified;
116 u64 vram_gpa;
117} __packed;
118
119struct synthvid_vram_location_ack {
120 u64 user_ctx;
121} __packed;
122
123struct video_output_situation {
124 u8 active;
125 u32 vram_offset;
126 u8 depth_bits;
127 u32 width_pixels;
128 u32 height_pixels;
129 u32 pitch_bytes;
130} __packed;
131
132struct synthvid_situation_update {
133 u64 user_ctx;
134 u8 video_output_count;
135 struct video_output_situation video_output[1];
136} __packed;
137
138struct synthvid_situation_update_ack {
139 u64 user_ctx;
140} __packed;
141
142struct synthvid_pointer_position {
143 u8 is_visible;
144 u8 video_output;
145 s32 image_x;
146 s32 image_y;
147} __packed;
148
149
150#define CURSOR_MAX_X 96
151#define CURSOR_MAX_Y 96
152#define CURSOR_ARGB_PIXEL_SIZE 4
153#define CURSOR_MAX_SIZE (CURSOR_MAX_X * CURSOR_MAX_Y * CURSOR_ARGB_PIXEL_SIZE)
154#define CURSOR_COMPLETE (-1)
155
156struct synthvid_pointer_shape {
157 u8 part_idx;
158 u8 is_argb;
159 u32 width; /* CURSOR_MAX_X at most */
160 u32 height; /* CURSOR_MAX_Y at most */
161 u32 hot_x; /* hotspot relative to upper-left of pointer image */
162 u32 hot_y;
163 u8 data[4];
164} __packed;
165
166struct synthvid_feature_change {
167 u8 is_dirt_needed;
168 u8 is_ptr_pos_needed;
169 u8 is_ptr_shape_needed;
170 u8 is_situ_needed;
171} __packed;
172
173struct rect {
174 s32 x1, y1; /* top left corner */
175 s32 x2, y2; /* bottom right corner, exclusive */
176} __packed;
177
178struct synthvid_dirt {
179 u8 video_output;
180 u8 dirt_count;
181 struct rect rect[1];
182} __packed;
183
184struct synthvid_msg {
185 struct pipe_msg_hdr pipe_hdr;
186 struct synthvid_msg_hdr vid_hdr;
187 union {
188 struct synthvid_version_req ver_req;
189 struct synthvid_version_resp ver_resp;
190 struct synthvid_vram_location vram;
191 struct synthvid_vram_location_ack vram_ack;
192 struct synthvid_situation_update situ;
193 struct synthvid_situation_update_ack situ_ack;
194 struct synthvid_pointer_position ptr_pos;
195 struct synthvid_pointer_shape ptr_shape;
196 struct synthvid_feature_change feature_chg;
197 struct synthvid_dirt dirt;
198 };
199} __packed;
200
201
202
203/* FB driver definitions and structures */
204#define HVFB_WIDTH 1152 /* default screen width */
205#define HVFB_HEIGHT 864 /* default screen height */
206#define HVFB_WIDTH_MIN 640
207#define HVFB_HEIGHT_MIN 480
208
209#define RING_BUFSIZE (256 * 1024)
210#define VSP_TIMEOUT (10 * HZ)
211#define HVFB_UPDATE_DELAY (HZ / 20)
212
213struct hvfb_par {
214 struct fb_info *info;
215 bool fb_ready; /* fb device is ready */
216 struct completion wait;
217 u32 synthvid_version;
218
219 struct delayed_work dwork;
220 bool update;
221
222 u32 pseudo_palette[16];
223 u8 init_buf[MAX_VMBUS_PKT_SIZE];
224 u8 recv_buf[MAX_VMBUS_PKT_SIZE];
225};
226
227static uint screen_width = HVFB_WIDTH;
228static uint screen_height = HVFB_HEIGHT;
229static uint screen_depth;
230static uint screen_fb_size;
231
232/* Send message to Hyper-V host */
233static inline int synthvid_send(struct hv_device *hdev,
234 struct synthvid_msg *msg)
235{
236 static atomic64_t request_id = ATOMIC64_INIT(0);
237 int ret;
238
239 msg->pipe_hdr.type = PIPE_MSG_DATA;
240 msg->pipe_hdr.size = msg->vid_hdr.size;
241
242 ret = vmbus_sendpacket(hdev->channel, msg,
243 msg->vid_hdr.size + sizeof(struct pipe_msg_hdr),
244 atomic64_inc_return(&request_id),
245 VM_PKT_DATA_INBAND, 0);
246
247 if (ret)
248 pr_err("Unable to send packet via vmbus\n");
249
250 return ret;
251}
252
253
254/* Send screen resolution info to host */
255static int synthvid_send_situ(struct hv_device *hdev)
256{
257 struct fb_info *info = hv_get_drvdata(hdev);
258 struct synthvid_msg msg;
259
260 if (!info)
261 return -ENODEV;
262
263 memset(&msg, 0, sizeof(struct synthvid_msg));
264
265 msg.vid_hdr.type = SYNTHVID_SITUATION_UPDATE;
266 msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
267 sizeof(struct synthvid_situation_update);
268 msg.situ.user_ctx = 0;
269 msg.situ.video_output_count = 1;
270 msg.situ.video_output[0].active = 1;
271 msg.situ.video_output[0].vram_offset = 0;
272 msg.situ.video_output[0].depth_bits = info->var.bits_per_pixel;
273 msg.situ.video_output[0].width_pixels = info->var.xres;
274 msg.situ.video_output[0].height_pixels = info->var.yres;
275 msg.situ.video_output[0].pitch_bytes = info->fix.line_length;
276
277 synthvid_send(hdev, &msg);
278
279 return 0;
280}
281
282/* Send mouse pointer info to host */
283static int synthvid_send_ptr(struct hv_device *hdev)
284{
285 struct synthvid_msg msg;
286
287 memset(&msg, 0, sizeof(struct synthvid_msg));
288 msg.vid_hdr.type = SYNTHVID_POINTER_POSITION;
289 msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
290 sizeof(struct synthvid_pointer_position);
291 msg.ptr_pos.is_visible = 1;
292 msg.ptr_pos.video_output = 0;
293 msg.ptr_pos.image_x = 0;
294 msg.ptr_pos.image_y = 0;
295 synthvid_send(hdev, &msg);
296
297 memset(&msg, 0, sizeof(struct synthvid_msg));
298 msg.vid_hdr.type = SYNTHVID_POINTER_SHAPE;
299 msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
300 sizeof(struct synthvid_pointer_shape);
301 msg.ptr_shape.part_idx = CURSOR_COMPLETE;
302 msg.ptr_shape.is_argb = 1;
303 msg.ptr_shape.width = 1;
304 msg.ptr_shape.height = 1;
305 msg.ptr_shape.hot_x = 0;
306 msg.ptr_shape.hot_y = 0;
307 msg.ptr_shape.data[0] = 0;
308 msg.ptr_shape.data[1] = 1;
309 msg.ptr_shape.data[2] = 1;
310 msg.ptr_shape.data[3] = 1;
311 synthvid_send(hdev, &msg);
312
313 return 0;
314}
315
316/* Send updated screen area (dirty rectangle) location to host */
317static int synthvid_update(struct fb_info *info)
318{
319 struct hv_device *hdev = device_to_hv_device(info->device);
320 struct synthvid_msg msg;
321
322 memset(&msg, 0, sizeof(struct synthvid_msg));
323
324 msg.vid_hdr.type = SYNTHVID_DIRT;
325 msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
326 sizeof(struct synthvid_dirt);
327 msg.dirt.video_output = 0;
328 msg.dirt.dirt_count = 1;
329 msg.dirt.rect[0].x1 = 0;
330 msg.dirt.rect[0].y1 = 0;
331 msg.dirt.rect[0].x2 = info->var.xres;
332 msg.dirt.rect[0].y2 = info->var.yres;
333
334 synthvid_send(hdev, &msg);
335
336 return 0;
337}
338
339
340/*
341 * Actions on received messages from host:
342 * Complete the wait event.
343 * Or, reply with screen and cursor info.
344 */
345static void synthvid_recv_sub(struct hv_device *hdev)
346{
347 struct fb_info *info = hv_get_drvdata(hdev);
348 struct hvfb_par *par;
349 struct synthvid_msg *msg;
350
351 if (!info)
352 return;
353
354 par = info->par;
355 msg = (struct synthvid_msg *)par->recv_buf;
356
357 /* Complete the wait event */
358 if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE ||
359 msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) {
360 memcpy(par->init_buf, msg, MAX_VMBUS_PKT_SIZE);
361 complete(&par->wait);
362 return;
363 }
364
365 /* Reply with screen and cursor info */
366 if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE) {
367 if (par->fb_ready) {
368 synthvid_send_ptr(hdev);
369 synthvid_send_situ(hdev);
370 }
371
372 par->update = msg->feature_chg.is_dirt_needed;
373 if (par->update)
374 schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY);
375 }
376}
377
378/* Receive callback for messages from the host */
379static void synthvid_receive(void *ctx)
380{
381 struct hv_device *hdev = ctx;
382 struct fb_info *info = hv_get_drvdata(hdev);
383 struct hvfb_par *par;
384 struct synthvid_msg *recv_buf;
385 u32 bytes_recvd;
386 u64 req_id;
387 int ret;
388
389 if (!info)
390 return;
391
392 par = info->par;
393 recv_buf = (struct synthvid_msg *)par->recv_buf;
394
395 do {
396 ret = vmbus_recvpacket(hdev->channel, recv_buf,
397 MAX_VMBUS_PKT_SIZE,
398 &bytes_recvd, &req_id);
399 if (bytes_recvd > 0 &&
400 recv_buf->pipe_hdr.type == PIPE_MSG_DATA)
401 synthvid_recv_sub(hdev);
402 } while (bytes_recvd > 0 && ret == 0);
403}
404
405/* Check synthetic video protocol version with the host */
406static int synthvid_negotiate_ver(struct hv_device *hdev, u32 ver)
407{
408 struct fb_info *info = hv_get_drvdata(hdev);
409 struct hvfb_par *par = info->par;
410 struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf;
411 int t, ret = 0;
412
413 memset(msg, 0, sizeof(struct synthvid_msg));
414 msg->vid_hdr.type = SYNTHVID_VERSION_REQUEST;
415 msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
416 sizeof(struct synthvid_version_req);
417 msg->ver_req.version = ver;
418 synthvid_send(hdev, msg);
419
420 t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT);
421 if (!t) {
422 pr_err("Time out on waiting version response\n");
423 ret = -ETIMEDOUT;
424 goto out;
425 }
426 if (!msg->ver_resp.is_accepted) {
427 ret = -ENODEV;
428 goto out;
429 }
430
431 par->synthvid_version = ver;
432
433out:
434 return ret;
435}
436
437/* Connect to VSP (Virtual Service Provider) on host */
438static int synthvid_connect_vsp(struct hv_device *hdev)
439{
440 struct fb_info *info = hv_get_drvdata(hdev);
441 struct hvfb_par *par = info->par;
442 int ret;
443
444 ret = vmbus_open(hdev->channel, RING_BUFSIZE, RING_BUFSIZE,
445 NULL, 0, synthvid_receive, hdev);
446 if (ret) {
447 pr_err("Unable to open vmbus channel\n");
448 return ret;
449 }
450
451 /* Negotiate the protocol version with host */
452 if (vmbus_proto_version == VERSION_WS2008 ||
453 vmbus_proto_version == VERSION_WIN7)
454 ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN7);
455 else
456 ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN8);
457
458 if (ret) {
459 pr_err("Synthetic video device version not accepted\n");
460 goto error;
461 }
462
463 if (par->synthvid_version == SYNTHVID_VERSION_WIN7) {
464 screen_depth = SYNTHVID_DEPTH_WIN7;
465 screen_fb_size = SYNTHVID_FB_SIZE_WIN7;
466 } else {
467 screen_depth = SYNTHVID_DEPTH_WIN8;
468 screen_fb_size = SYNTHVID_FB_SIZE_WIN8;
469 }
470
471 return 0;
472
473error:
474 vmbus_close(hdev->channel);
475 return ret;
476}
477
478/* Send VRAM and Situation messages to the host */
479static int synthvid_send_config(struct hv_device *hdev)
480{
481 struct fb_info *info = hv_get_drvdata(hdev);
482 struct hvfb_par *par = info->par;
483 struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf;
484 int t, ret = 0;
485
486 /* Send VRAM location */
487 memset(msg, 0, sizeof(struct synthvid_msg));
488 msg->vid_hdr.type = SYNTHVID_VRAM_LOCATION;
489 msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
490 sizeof(struct synthvid_vram_location);
491 msg->vram.user_ctx = msg->vram.vram_gpa = info->fix.smem_start;
492 msg->vram.is_vram_gpa_specified = 1;
493 synthvid_send(hdev, msg);
494
495 t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT);
496 if (!t) {
497 pr_err("Time out on waiting vram location ack\n");
498 ret = -ETIMEDOUT;
499 goto out;
500 }
501 if (msg->vram_ack.user_ctx != info->fix.smem_start) {
502 pr_err("Unable to set VRAM location\n");
503 ret = -ENODEV;
504 goto out;
505 }
506
507 /* Send pointer and situation update */
508 synthvid_send_ptr(hdev);
509 synthvid_send_situ(hdev);
510
511out:
512 return ret;
513}
514
515
516/*
517 * Delayed work callback:
518 * It is called at HVFB_UPDATE_DELAY or longer time interval to process
519 * screen updates. It is re-scheduled if further update is necessary.
520 */
521static void hvfb_update_work(struct work_struct *w)
522{
523 struct hvfb_par *par = container_of(w, struct hvfb_par, dwork.work);
524 struct fb_info *info = par->info;
525
526 if (par->fb_ready)
527 synthvid_update(info);
528
529 if (par->update)
530 schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY);
531}
532
533
534/* Framebuffer operation handlers */
535
536static int hvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
537{
538 if (var->xres < HVFB_WIDTH_MIN || var->yres < HVFB_HEIGHT_MIN ||
539 var->xres > screen_width || var->yres > screen_height ||
540 var->bits_per_pixel != screen_depth)
541 return -EINVAL;
542
543 var->xres_virtual = var->xres;
544 var->yres_virtual = var->yres;
545
546 return 0;
547}
548
549static int hvfb_set_par(struct fb_info *info)
550{
551 struct hv_device *hdev = device_to_hv_device(info->device);
552
553 return synthvid_send_situ(hdev);
554}
555
556
557static inline u32 chan_to_field(u32 chan, struct fb_bitfield *bf)
558{
559 return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
560}
561
562static int hvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
563 unsigned blue, unsigned transp, struct fb_info *info)
564{
565 u32 *pal = info->pseudo_palette;
566
567 if (regno > 15)
568 return -EINVAL;
569
570 pal[regno] = chan_to_field(red, &info->var.red)
571 | chan_to_field(green, &info->var.green)
572 | chan_to_field(blue, &info->var.blue)
573 | chan_to_field(transp, &info->var.transp);
574
575 return 0;
576}
577
578
579static struct fb_ops hvfb_ops = {
580 .owner = THIS_MODULE,
581 .fb_check_var = hvfb_check_var,
582 .fb_set_par = hvfb_set_par,
583 .fb_setcolreg = hvfb_setcolreg,
584 .fb_fillrect = cfb_fillrect,
585 .fb_copyarea = cfb_copyarea,
586 .fb_imageblit = cfb_imageblit,
587};
588
589
590/* Get options from kernel paramenter "video=" */
591static void hvfb_get_option(struct fb_info *info)
592{
593 struct hvfb_par *par = info->par;
594 char *opt = NULL, *p;
595 uint x = 0, y = 0;
596
597 if (fb_get_options(KBUILD_MODNAME, &opt) || !opt || !*opt)
598 return;
599
600 p = strsep(&opt, "x");
601 if (!*p || kstrtouint(p, 0, &x) ||
602 !opt || !*opt || kstrtouint(opt, 0, &y)) {
603 pr_err("Screen option is invalid: skipped\n");
604 return;
605 }
606
607 if (x < HVFB_WIDTH_MIN || y < HVFB_HEIGHT_MIN ||
608 (par->synthvid_version == SYNTHVID_VERSION_WIN8 &&
609 x * y * screen_depth / 8 > SYNTHVID_FB_SIZE_WIN8) ||
610 (par->synthvid_version == SYNTHVID_VERSION_WIN7 &&
611 (x > SYNTHVID_WIDTH_MAX_WIN7 || y > SYNTHVID_HEIGHT_MAX_WIN7))) {
612 pr_err("Screen resolution option is out of range: skipped\n");
613 return;
614 }
615
616 screen_width = x;
617 screen_height = y;
618 return;
619}
620
621
622/* Get framebuffer memory from Hyper-V video pci space */
623static int hvfb_getmem(struct fb_info *info)
624{
625 struct pci_dev *pdev;
626 ulong fb_phys;
627 void __iomem *fb_virt;
628
629 pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
630 PCI_DEVICE_ID_HYPERV_VIDEO, NULL);
631 if (!pdev) {
632 pr_err("Unable to find PCI Hyper-V video\n");
633 return -ENODEV;
634 }
635
636 if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
637 pci_resource_len(pdev, 0) < screen_fb_size)
638 goto err1;
639
640 fb_phys = pci_resource_end(pdev, 0) - screen_fb_size + 1;
641 if (!request_mem_region(fb_phys, screen_fb_size, KBUILD_MODNAME))
642 goto err1;
643
644 fb_virt = ioremap(fb_phys, screen_fb_size);
645 if (!fb_virt)
646 goto err2;
647
648 info->apertures = alloc_apertures(1);
649 if (!info->apertures)
650 goto err3;
651
652 info->apertures->ranges[0].base = pci_resource_start(pdev, 0);
653 info->apertures->ranges[0].size = pci_resource_len(pdev, 0);
654 info->fix.smem_start = fb_phys;
655 info->fix.smem_len = screen_fb_size;
656 info->screen_base = fb_virt;
657 info->screen_size = screen_fb_size;
658
659 pci_dev_put(pdev);
660 return 0;
661
662err3:
663 iounmap(fb_virt);
664err2:
665 release_mem_region(fb_phys, screen_fb_size);
666err1:
667 pci_dev_put(pdev);
668 return -ENOMEM;
669}
670
671/* Release the framebuffer */
672static void hvfb_putmem(struct fb_info *info)
673{
674 iounmap(info->screen_base);
675 release_mem_region(info->fix.smem_start, screen_fb_size);
676}
677
678
679static int hvfb_probe(struct hv_device *hdev,
680 const struct hv_vmbus_device_id *dev_id)
681{
682 struct fb_info *info;
683 struct hvfb_par *par;
684 int ret;
685
686 info = framebuffer_alloc(sizeof(struct hvfb_par), &hdev->device);
687 if (!info) {
688 pr_err("No memory for framebuffer info\n");
689 return -ENOMEM;
690 }
691
692 par = info->par;
693 par->info = info;
694 par->fb_ready = false;
695 init_completion(&par->wait);
696 INIT_DELAYED_WORK(&par->dwork, hvfb_update_work);
697
698 /* Connect to VSP */
699 hv_set_drvdata(hdev, info);
700 ret = synthvid_connect_vsp(hdev);
701 if (ret) {
702 pr_err("Unable to connect to VSP\n");
703 goto error1;
704 }
705
706 ret = hvfb_getmem(info);
707 if (ret) {
708 pr_err("No memory for framebuffer\n");
709 goto error2;
710 }
711
712 hvfb_get_option(info);
713 pr_info("Screen resolution: %dx%d, Color depth: %d\n",
714 screen_width, screen_height, screen_depth);
715
716
717 /* Set up fb_info */
718 info->flags = FBINFO_DEFAULT;
719
720 info->var.xres_virtual = info->var.xres = screen_width;
721 info->var.yres_virtual = info->var.yres = screen_height;
722 info->var.bits_per_pixel = screen_depth;
723
724 if (info->var.bits_per_pixel == 16) {
725 info->var.red = (struct fb_bitfield){11, 5, 0};
726 info->var.green = (struct fb_bitfield){5, 6, 0};
727 info->var.blue = (struct fb_bitfield){0, 5, 0};
728 info->var.transp = (struct fb_bitfield){0, 0, 0};
729 } else {
730 info->var.red = (struct fb_bitfield){16, 8, 0};
731 info->var.green = (struct fb_bitfield){8, 8, 0};
732 info->var.blue = (struct fb_bitfield){0, 8, 0};
733 info->var.transp = (struct fb_bitfield){24, 8, 0};
734 }
735
736 info->var.activate = FB_ACTIVATE_NOW;
737 info->var.height = -1;
738 info->var.width = -1;
739 info->var.vmode = FB_VMODE_NONINTERLACED;
740
741 strcpy(info->fix.id, KBUILD_MODNAME);
742 info->fix.type = FB_TYPE_PACKED_PIXELS;
743 info->fix.visual = FB_VISUAL_TRUECOLOR;
744 info->fix.line_length = screen_width * screen_depth / 8;
745 info->fix.accel = FB_ACCEL_NONE;
746
747 info->fbops = &hvfb_ops;
748 info->pseudo_palette = par->pseudo_palette;
749
750 /* Send config to host */
751 ret = synthvid_send_config(hdev);
752 if (ret)
753 goto error;
754
755 ret = register_framebuffer(info);
756 if (ret) {
757 pr_err("Unable to register framebuffer\n");
758 goto error;
759 }
760
761 par->fb_ready = true;
762
763 return 0;
764
765error:
766 hvfb_putmem(info);
767error2:
768 vmbus_close(hdev->channel);
769error1:
770 cancel_delayed_work_sync(&par->dwork);
771 hv_set_drvdata(hdev, NULL);
772 framebuffer_release(info);
773 return ret;
774}
775
776
777static int hvfb_remove(struct hv_device *hdev)
778{
779 struct fb_info *info = hv_get_drvdata(hdev);
780 struct hvfb_par *par = info->par;
781
782 par->update = false;
783 par->fb_ready = false;
784
785 unregister_framebuffer(info);
786 cancel_delayed_work_sync(&par->dwork);
787
788 vmbus_close(hdev->channel);
789 hv_set_drvdata(hdev, NULL);
790
791 hvfb_putmem(info);
792 framebuffer_release(info);
793
794 return 0;
795}
796
797
798static const struct hv_vmbus_device_id id_table[] = {
799 /* Synthetic Video Device GUID */
800 {HV_SYNTHVID_GUID},
801 {}
802};
803
804MODULE_DEVICE_TABLE(vmbus, id_table);
805
806static struct hv_driver hvfb_drv = {
807 .name = KBUILD_MODNAME,
808 .id_table = id_table,
809 .probe = hvfb_probe,
810 .remove = hvfb_remove,
811};
812
813
814static int __init hvfb_drv_init(void)
815{
816 return vmbus_driver_register(&hvfb_drv);
817}
818
819static void __exit hvfb_drv_exit(void)
820{
821 vmbus_driver_unregister(&hvfb_drv);
822}
823
824module_init(hvfb_drv_init);
825module_exit(hvfb_drv_exit);
826
827MODULE_LICENSE("GPL");
828MODULE_VERSION(HV_DRV_VERSION);
829MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Video Frame Buffer Driver");