diff options
Diffstat (limited to 'drivers/gpu/drm/bochs')
-rw-r--r-- | drivers/gpu/drm/bochs/Kconfig | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/bochs/Makefile | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/bochs/bochs.h | 164 | ||||
-rw-r--r-- | drivers/gpu/drm/bochs/bochs_drv.c | 178 | ||||
-rw-r--r-- | drivers/gpu/drm/bochs/bochs_fbdev.c | 215 | ||||
-rw-r--r-- | drivers/gpu/drm/bochs/bochs_hw.c | 177 | ||||
-rw-r--r-- | drivers/gpu/drm/bochs/bochs_kms.c | 294 | ||||
-rw-r--r-- | drivers/gpu/drm/bochs/bochs_mm.c | 546 |
8 files changed, 1589 insertions, 0 deletions
diff --git a/drivers/gpu/drm/bochs/Kconfig b/drivers/gpu/drm/bochs/Kconfig new file mode 100644 index 000000000000..c8fcf12019f0 --- /dev/null +++ b/drivers/gpu/drm/bochs/Kconfig | |||
@@ -0,0 +1,11 @@ | |||
1 | config DRM_BOCHS | ||
2 | tristate "DRM Support for bochs dispi vga interface (qemu stdvga)" | ||
3 | depends on DRM && PCI | ||
4 | select DRM_KMS_HELPER | ||
5 | select FB_SYS_FILLRECT | ||
6 | select FB_SYS_COPYAREA | ||
7 | select FB_SYS_IMAGEBLIT | ||
8 | select DRM_TTM | ||
9 | help | ||
10 | Choose this option for qemu. | ||
11 | If M is selected the module will be called bochs-drm. | ||
diff --git a/drivers/gpu/drm/bochs/Makefile b/drivers/gpu/drm/bochs/Makefile new file mode 100644 index 000000000000..844a55614920 --- /dev/null +++ b/drivers/gpu/drm/bochs/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | ccflags-y := -Iinclude/drm | ||
2 | bochs-drm-y := bochs_drv.o bochs_mm.o bochs_kms.o bochs_fbdev.o bochs_hw.o | ||
3 | |||
4 | obj-$(CONFIG_DRM_BOCHS) += bochs-drm.o | ||
diff --git a/drivers/gpu/drm/bochs/bochs.h b/drivers/gpu/drm/bochs/bochs.h new file mode 100644 index 000000000000..741965c001a6 --- /dev/null +++ b/drivers/gpu/drm/bochs/bochs.h | |||
@@ -0,0 +1,164 @@ | |||
1 | #include <linux/io.h> | ||
2 | #include <linux/fb.h> | ||
3 | |||
4 | #include <drm/drmP.h> | ||
5 | #include <drm/drm_crtc.h> | ||
6 | #include <drm/drm_crtc_helper.h> | ||
7 | #include <drm/drm_fb_helper.h> | ||
8 | |||
9 | #include <ttm/ttm_bo_driver.h> | ||
10 | #include <ttm/ttm_page_alloc.h> | ||
11 | |||
12 | /* ---------------------------------------------------------------------- */ | ||
13 | |||
14 | #define VBE_DISPI_IOPORT_INDEX 0x01CE | ||
15 | #define VBE_DISPI_IOPORT_DATA 0x01CF | ||
16 | |||
17 | #define VBE_DISPI_INDEX_ID 0x0 | ||
18 | #define VBE_DISPI_INDEX_XRES 0x1 | ||
19 | #define VBE_DISPI_INDEX_YRES 0x2 | ||
20 | #define VBE_DISPI_INDEX_BPP 0x3 | ||
21 | #define VBE_DISPI_INDEX_ENABLE 0x4 | ||
22 | #define VBE_DISPI_INDEX_BANK 0x5 | ||
23 | #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 | ||
24 | #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 | ||
25 | #define VBE_DISPI_INDEX_X_OFFSET 0x8 | ||
26 | #define VBE_DISPI_INDEX_Y_OFFSET 0x9 | ||
27 | #define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa | ||
28 | |||
29 | #define VBE_DISPI_ID0 0xB0C0 | ||
30 | #define VBE_DISPI_ID1 0xB0C1 | ||
31 | #define VBE_DISPI_ID2 0xB0C2 | ||
32 | #define VBE_DISPI_ID3 0xB0C3 | ||
33 | #define VBE_DISPI_ID4 0xB0C4 | ||
34 | #define VBE_DISPI_ID5 0xB0C5 | ||
35 | |||
36 | #define VBE_DISPI_DISABLED 0x00 | ||
37 | #define VBE_DISPI_ENABLED 0x01 | ||
38 | #define VBE_DISPI_GETCAPS 0x02 | ||
39 | #define VBE_DISPI_8BIT_DAC 0x20 | ||
40 | #define VBE_DISPI_LFB_ENABLED 0x40 | ||
41 | #define VBE_DISPI_NOCLEARMEM 0x80 | ||
42 | |||
43 | /* ---------------------------------------------------------------------- */ | ||
44 | |||
45 | enum bochs_types { | ||
46 | BOCHS_QEMU_STDVGA, | ||
47 | BOCHS_UNKNOWN, | ||
48 | }; | ||
49 | |||
50 | struct bochs_framebuffer { | ||
51 | struct drm_framebuffer base; | ||
52 | struct drm_gem_object *obj; | ||
53 | }; | ||
54 | |||
55 | struct bochs_device { | ||
56 | /* hw */ | ||
57 | void __iomem *mmio; | ||
58 | int ioports; | ||
59 | void __iomem *fb_map; | ||
60 | unsigned long fb_base; | ||
61 | unsigned long fb_size; | ||
62 | |||
63 | /* mode */ | ||
64 | u16 xres; | ||
65 | u16 yres; | ||
66 | u16 yres_virtual; | ||
67 | u32 stride; | ||
68 | u32 bpp; | ||
69 | |||
70 | /* drm */ | ||
71 | struct drm_device *dev; | ||
72 | struct drm_crtc crtc; | ||
73 | struct drm_encoder encoder; | ||
74 | struct drm_connector connector; | ||
75 | bool mode_config_initialized; | ||
76 | |||
77 | /* ttm */ | ||
78 | struct { | ||
79 | struct drm_global_reference mem_global_ref; | ||
80 | struct ttm_bo_global_ref bo_global_ref; | ||
81 | struct ttm_bo_device bdev; | ||
82 | bool initialized; | ||
83 | } ttm; | ||
84 | |||
85 | /* fbdev */ | ||
86 | struct { | ||
87 | struct bochs_framebuffer gfb; | ||
88 | struct drm_fb_helper helper; | ||
89 | int size; | ||
90 | int x1, y1, x2, y2; /* dirty rect */ | ||
91 | spinlock_t dirty_lock; | ||
92 | bool initialized; | ||
93 | } fb; | ||
94 | }; | ||
95 | |||
96 | #define to_bochs_framebuffer(x) container_of(x, struct bochs_framebuffer, base) | ||
97 | |||
98 | struct bochs_bo { | ||
99 | struct ttm_buffer_object bo; | ||
100 | struct ttm_placement placement; | ||
101 | struct ttm_bo_kmap_obj kmap; | ||
102 | struct drm_gem_object gem; | ||
103 | u32 placements[3]; | ||
104 | int pin_count; | ||
105 | }; | ||
106 | |||
107 | static inline struct bochs_bo *bochs_bo(struct ttm_buffer_object *bo) | ||
108 | { | ||
109 | return container_of(bo, struct bochs_bo, bo); | ||
110 | } | ||
111 | |||
112 | static inline struct bochs_bo *gem_to_bochs_bo(struct drm_gem_object *gem) | ||
113 | { | ||
114 | return container_of(gem, struct bochs_bo, gem); | ||
115 | } | ||
116 | |||
117 | #define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) | ||
118 | |||
119 | static inline u64 bochs_bo_mmap_offset(struct bochs_bo *bo) | ||
120 | { | ||
121 | return drm_vma_node_offset_addr(&bo->bo.vma_node); | ||
122 | } | ||
123 | |||
124 | /* ---------------------------------------------------------------------- */ | ||
125 | |||
126 | /* bochs_hw.c */ | ||
127 | int bochs_hw_init(struct drm_device *dev, uint32_t flags); | ||
128 | void bochs_hw_fini(struct drm_device *dev); | ||
129 | |||
130 | void bochs_hw_setmode(struct bochs_device *bochs, | ||
131 | struct drm_display_mode *mode); | ||
132 | void bochs_hw_setbase(struct bochs_device *bochs, | ||
133 | int x, int y, u64 addr); | ||
134 | |||
135 | /* bochs_mm.c */ | ||
136 | int bochs_mm_init(struct bochs_device *bochs); | ||
137 | void bochs_mm_fini(struct bochs_device *bochs); | ||
138 | int bochs_mmap(struct file *filp, struct vm_area_struct *vma); | ||
139 | |||
140 | int bochs_gem_create(struct drm_device *dev, u32 size, bool iskernel, | ||
141 | struct drm_gem_object **obj); | ||
142 | int bochs_gem_init_object(struct drm_gem_object *obj); | ||
143 | void bochs_gem_free_object(struct drm_gem_object *obj); | ||
144 | int bochs_dumb_create(struct drm_file *file, struct drm_device *dev, | ||
145 | struct drm_mode_create_dumb *args); | ||
146 | int bochs_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev, | ||
147 | uint32_t handle, uint64_t *offset); | ||
148 | |||
149 | int bochs_framebuffer_init(struct drm_device *dev, | ||
150 | struct bochs_framebuffer *gfb, | ||
151 | struct drm_mode_fb_cmd2 *mode_cmd, | ||
152 | struct drm_gem_object *obj); | ||
153 | int bochs_bo_pin(struct bochs_bo *bo, u32 pl_flag, u64 *gpu_addr); | ||
154 | int bochs_bo_unpin(struct bochs_bo *bo); | ||
155 | |||
156 | extern const struct drm_mode_config_funcs bochs_mode_funcs; | ||
157 | |||
158 | /* bochs_kms.c */ | ||
159 | int bochs_kms_init(struct bochs_device *bochs); | ||
160 | void bochs_kms_fini(struct bochs_device *bochs); | ||
161 | |||
162 | /* bochs_fbdev.c */ | ||
163 | int bochs_fbdev_init(struct bochs_device *bochs); | ||
164 | void bochs_fbdev_fini(struct bochs_device *bochs); | ||
diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c new file mode 100644 index 000000000000..395bba261c9a --- /dev/null +++ b/drivers/gpu/drm/bochs/bochs_drv.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License as published by | ||
4 | * the Free Software Foundation; either version 2 of the License, or | ||
5 | * (at your option) any later version. | ||
6 | */ | ||
7 | |||
8 | #include <linux/mm.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/slab.h> | ||
11 | |||
12 | #include "bochs.h" | ||
13 | |||
14 | static bool enable_fbdev = true; | ||
15 | module_param_named(fbdev, enable_fbdev, bool, 0444); | ||
16 | MODULE_PARM_DESC(fbdev, "register fbdev device"); | ||
17 | |||
18 | /* ---------------------------------------------------------------------- */ | ||
19 | /* drm interface */ | ||
20 | |||
21 | static int bochs_unload(struct drm_device *dev) | ||
22 | { | ||
23 | struct bochs_device *bochs = dev->dev_private; | ||
24 | |||
25 | bochs_fbdev_fini(bochs); | ||
26 | bochs_kms_fini(bochs); | ||
27 | bochs_mm_fini(bochs); | ||
28 | bochs_hw_fini(dev); | ||
29 | kfree(bochs); | ||
30 | dev->dev_private = NULL; | ||
31 | return 0; | ||
32 | } | ||
33 | |||
34 | static int bochs_load(struct drm_device *dev, unsigned long flags) | ||
35 | { | ||
36 | struct bochs_device *bochs; | ||
37 | int ret; | ||
38 | |||
39 | bochs = kzalloc(sizeof(*bochs), GFP_KERNEL); | ||
40 | if (bochs == NULL) | ||
41 | return -ENOMEM; | ||
42 | dev->dev_private = bochs; | ||
43 | bochs->dev = dev; | ||
44 | |||
45 | ret = bochs_hw_init(dev, flags); | ||
46 | if (ret) | ||
47 | goto err; | ||
48 | |||
49 | ret = bochs_mm_init(bochs); | ||
50 | if (ret) | ||
51 | goto err; | ||
52 | |||
53 | ret = bochs_kms_init(bochs); | ||
54 | if (ret) | ||
55 | goto err; | ||
56 | |||
57 | if (enable_fbdev) | ||
58 | bochs_fbdev_init(bochs); | ||
59 | |||
60 | return 0; | ||
61 | |||
62 | err: | ||
63 | bochs_unload(dev); | ||
64 | return ret; | ||
65 | } | ||
66 | |||
67 | static const struct file_operations bochs_fops = { | ||
68 | .owner = THIS_MODULE, | ||
69 | .open = drm_open, | ||
70 | .release = drm_release, | ||
71 | .unlocked_ioctl = drm_ioctl, | ||
72 | #ifdef CONFIG_COMPAT | ||
73 | .compat_ioctl = drm_compat_ioctl, | ||
74 | #endif | ||
75 | .poll = drm_poll, | ||
76 | .read = drm_read, | ||
77 | .llseek = no_llseek, | ||
78 | .mmap = bochs_mmap, | ||
79 | }; | ||
80 | |||
81 | static struct drm_driver bochs_driver = { | ||
82 | .driver_features = DRIVER_GEM | DRIVER_MODESET, | ||
83 | .load = bochs_load, | ||
84 | .unload = bochs_unload, | ||
85 | .fops = &bochs_fops, | ||
86 | .name = "bochs-drm", | ||
87 | .desc = "bochs dispi vga interface (qemu stdvga)", | ||
88 | .date = "20130925", | ||
89 | .major = 1, | ||
90 | .minor = 0, | ||
91 | .gem_free_object = bochs_gem_free_object, | ||
92 | .dumb_create = bochs_dumb_create, | ||
93 | .dumb_map_offset = bochs_dumb_mmap_offset, | ||
94 | .dumb_destroy = drm_gem_dumb_destroy, | ||
95 | }; | ||
96 | |||
97 | /* ---------------------------------------------------------------------- */ | ||
98 | /* pci interface */ | ||
99 | |||
100 | static int bochs_kick_out_firmware_fb(struct pci_dev *pdev) | ||
101 | { | ||
102 | struct apertures_struct *ap; | ||
103 | |||
104 | ap = alloc_apertures(1); | ||
105 | if (!ap) | ||
106 | return -ENOMEM; | ||
107 | |||
108 | ap->ranges[0].base = pci_resource_start(pdev, 0); | ||
109 | ap->ranges[0].size = pci_resource_len(pdev, 0); | ||
110 | remove_conflicting_framebuffers(ap, "bochsdrmfb", false); | ||
111 | kfree(ap); | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static int bochs_pci_probe(struct pci_dev *pdev, | ||
117 | const struct pci_device_id *ent) | ||
118 | { | ||
119 | int ret; | ||
120 | |||
121 | ret = bochs_kick_out_firmware_fb(pdev); | ||
122 | if (ret) | ||
123 | return ret; | ||
124 | |||
125 | return drm_get_pci_dev(pdev, ent, &bochs_driver); | ||
126 | } | ||
127 | |||
128 | static void bochs_pci_remove(struct pci_dev *pdev) | ||
129 | { | ||
130 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
131 | |||
132 | drm_put_dev(dev); | ||
133 | } | ||
134 | |||
135 | static DEFINE_PCI_DEVICE_TABLE(bochs_pci_tbl) = { | ||
136 | { | ||
137 | .vendor = 0x1234, | ||
138 | .device = 0x1111, | ||
139 | .subvendor = 0x1af4, | ||
140 | .subdevice = 0x1100, | ||
141 | .driver_data = BOCHS_QEMU_STDVGA, | ||
142 | }, | ||
143 | { | ||
144 | .vendor = 0x1234, | ||
145 | .device = 0x1111, | ||
146 | .subvendor = PCI_ANY_ID, | ||
147 | .subdevice = PCI_ANY_ID, | ||
148 | .driver_data = BOCHS_UNKNOWN, | ||
149 | }, | ||
150 | { /* end of list */ } | ||
151 | }; | ||
152 | |||
153 | static struct pci_driver bochs_pci_driver = { | ||
154 | .name = "bochs-drm", | ||
155 | .id_table = bochs_pci_tbl, | ||
156 | .probe = bochs_pci_probe, | ||
157 | .remove = bochs_pci_remove, | ||
158 | }; | ||
159 | |||
160 | /* ---------------------------------------------------------------------- */ | ||
161 | /* module init/exit */ | ||
162 | |||
163 | static int __init bochs_init(void) | ||
164 | { | ||
165 | return drm_pci_init(&bochs_driver, &bochs_pci_driver); | ||
166 | } | ||
167 | |||
168 | static void __exit bochs_exit(void) | ||
169 | { | ||
170 | drm_pci_exit(&bochs_driver, &bochs_pci_driver); | ||
171 | } | ||
172 | |||
173 | module_init(bochs_init); | ||
174 | module_exit(bochs_exit); | ||
175 | |||
176 | MODULE_DEVICE_TABLE(pci, bochs_pci_tbl); | ||
177 | MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>"); | ||
178 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c new file mode 100644 index 000000000000..4da5206b7cc9 --- /dev/null +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c | |||
@@ -0,0 +1,215 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License as published by | ||
4 | * the Free Software Foundation; either version 2 of the License, or | ||
5 | * (at your option) any later version. | ||
6 | */ | ||
7 | |||
8 | #include "bochs.h" | ||
9 | |||
10 | /* ---------------------------------------------------------------------- */ | ||
11 | |||
12 | static struct fb_ops bochsfb_ops = { | ||
13 | .owner = THIS_MODULE, | ||
14 | .fb_check_var = drm_fb_helper_check_var, | ||
15 | .fb_set_par = drm_fb_helper_set_par, | ||
16 | .fb_fillrect = sys_fillrect, | ||
17 | .fb_copyarea = sys_copyarea, | ||
18 | .fb_imageblit = sys_imageblit, | ||
19 | .fb_pan_display = drm_fb_helper_pan_display, | ||
20 | .fb_blank = drm_fb_helper_blank, | ||
21 | .fb_setcmap = drm_fb_helper_setcmap, | ||
22 | }; | ||
23 | |||
24 | static int bochsfb_create_object(struct bochs_device *bochs, | ||
25 | struct drm_mode_fb_cmd2 *mode_cmd, | ||
26 | struct drm_gem_object **gobj_p) | ||
27 | { | ||
28 | struct drm_device *dev = bochs->dev; | ||
29 | struct drm_gem_object *gobj; | ||
30 | u32 size; | ||
31 | int ret = 0; | ||
32 | |||
33 | size = mode_cmd->pitches[0] * mode_cmd->height; | ||
34 | ret = bochs_gem_create(dev, size, true, &gobj); | ||
35 | if (ret) | ||
36 | return ret; | ||
37 | |||
38 | *gobj_p = gobj; | ||
39 | return ret; | ||
40 | } | ||
41 | |||
42 | static int bochsfb_create(struct drm_fb_helper *helper, | ||
43 | struct drm_fb_helper_surface_size *sizes) | ||
44 | { | ||
45 | struct bochs_device *bochs = | ||
46 | container_of(helper, struct bochs_device, fb.helper); | ||
47 | struct drm_device *dev = bochs->dev; | ||
48 | struct fb_info *info; | ||
49 | struct drm_framebuffer *fb; | ||
50 | struct drm_mode_fb_cmd2 mode_cmd; | ||
51 | struct device *device = &dev->pdev->dev; | ||
52 | struct drm_gem_object *gobj = NULL; | ||
53 | struct bochs_bo *bo = NULL; | ||
54 | int size, ret; | ||
55 | |||
56 | if (sizes->surface_bpp != 32) | ||
57 | return -EINVAL; | ||
58 | |||
59 | mode_cmd.width = sizes->surface_width; | ||
60 | mode_cmd.height = sizes->surface_height; | ||
61 | mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8); | ||
62 | mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | ||
63 | sizes->surface_depth); | ||
64 | size = mode_cmd.pitches[0] * mode_cmd.height; | ||
65 | |||
66 | /* alloc, pin & map bo */ | ||
67 | ret = bochsfb_create_object(bochs, &mode_cmd, &gobj); | ||
68 | if (ret) { | ||
69 | DRM_ERROR("failed to create fbcon backing object %d\n", ret); | ||
70 | return ret; | ||
71 | } | ||
72 | |||
73 | bo = gem_to_bochs_bo(gobj); | ||
74 | |||
75 | ret = ttm_bo_reserve(&bo->bo, true, false, false, 0); | ||
76 | if (ret) | ||
77 | return ret; | ||
78 | |||
79 | ret = bochs_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL); | ||
80 | if (ret) { | ||
81 | DRM_ERROR("failed to pin fbcon\n"); | ||
82 | ttm_bo_unreserve(&bo->bo); | ||
83 | return ret; | ||
84 | } | ||
85 | |||
86 | ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, | ||
87 | &bo->kmap); | ||
88 | if (ret) { | ||
89 | DRM_ERROR("failed to kmap fbcon\n"); | ||
90 | ttm_bo_unreserve(&bo->bo); | ||
91 | return ret; | ||
92 | } | ||
93 | |||
94 | ttm_bo_unreserve(&bo->bo); | ||
95 | |||
96 | /* init fb device */ | ||
97 | info = framebuffer_alloc(0, device); | ||
98 | if (info == NULL) | ||
99 | return -ENOMEM; | ||
100 | |||
101 | info->par = &bochs->fb.helper; | ||
102 | |||
103 | ret = bochs_framebuffer_init(bochs->dev, &bochs->fb.gfb, &mode_cmd, gobj); | ||
104 | if (ret) | ||
105 | return ret; | ||
106 | |||
107 | bochs->fb.size = size; | ||
108 | |||
109 | /* setup helper */ | ||
110 | fb = &bochs->fb.gfb.base; | ||
111 | bochs->fb.helper.fb = fb; | ||
112 | bochs->fb.helper.fbdev = info; | ||
113 | |||
114 | strcpy(info->fix.id, "bochsdrmfb"); | ||
115 | |||
116 | info->flags = FBINFO_DEFAULT; | ||
117 | info->fbops = &bochsfb_ops; | ||
118 | |||
119 | drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); | ||
120 | drm_fb_helper_fill_var(info, &bochs->fb.helper, sizes->fb_width, | ||
121 | sizes->fb_height); | ||
122 | |||
123 | info->screen_base = bo->kmap.virtual; | ||
124 | info->screen_size = size; | ||
125 | |||
126 | #if 0 | ||
127 | /* FIXME: get this right for mmap(/dev/fb0) */ | ||
128 | info->fix.smem_start = bochs_bo_mmap_offset(bo); | ||
129 | info->fix.smem_len = size; | ||
130 | #endif | ||
131 | |||
132 | ret = fb_alloc_cmap(&info->cmap, 256, 0); | ||
133 | if (ret) { | ||
134 | DRM_ERROR("%s: can't allocate color map\n", info->fix.id); | ||
135 | return -ENOMEM; | ||
136 | } | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static int bochs_fbdev_destroy(struct bochs_device *bochs) | ||
142 | { | ||
143 | struct bochs_framebuffer *gfb = &bochs->fb.gfb; | ||
144 | struct fb_info *info; | ||
145 | |||
146 | DRM_DEBUG_DRIVER("\n"); | ||
147 | |||
148 | if (bochs->fb.helper.fbdev) { | ||
149 | info = bochs->fb.helper.fbdev; | ||
150 | |||
151 | unregister_framebuffer(info); | ||
152 | if (info->cmap.len) | ||
153 | fb_dealloc_cmap(&info->cmap); | ||
154 | framebuffer_release(info); | ||
155 | } | ||
156 | |||
157 | if (gfb->obj) { | ||
158 | drm_gem_object_unreference_unlocked(gfb->obj); | ||
159 | gfb->obj = NULL; | ||
160 | } | ||
161 | |||
162 | drm_fb_helper_fini(&bochs->fb.helper); | ||
163 | drm_framebuffer_unregister_private(&gfb->base); | ||
164 | drm_framebuffer_cleanup(&gfb->base); | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | void bochs_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, | ||
170 | u16 blue, int regno) | ||
171 | { | ||
172 | } | ||
173 | |||
174 | void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, | ||
175 | u16 *blue, int regno) | ||
176 | { | ||
177 | *red = regno; | ||
178 | *green = regno; | ||
179 | *blue = regno; | ||
180 | } | ||
181 | |||
182 | static struct drm_fb_helper_funcs bochs_fb_helper_funcs = { | ||
183 | .gamma_set = bochs_fb_gamma_set, | ||
184 | .gamma_get = bochs_fb_gamma_get, | ||
185 | .fb_probe = bochsfb_create, | ||
186 | }; | ||
187 | |||
188 | int bochs_fbdev_init(struct bochs_device *bochs) | ||
189 | { | ||
190 | int ret; | ||
191 | |||
192 | bochs->fb.helper.funcs = &bochs_fb_helper_funcs; | ||
193 | spin_lock_init(&bochs->fb.dirty_lock); | ||
194 | |||
195 | ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper, | ||
196 | 1, 1); | ||
197 | if (ret) | ||
198 | return ret; | ||
199 | |||
200 | drm_fb_helper_single_add_all_connectors(&bochs->fb.helper); | ||
201 | drm_helper_disable_unused_functions(bochs->dev); | ||
202 | drm_fb_helper_initial_config(&bochs->fb.helper, 32); | ||
203 | |||
204 | bochs->fb.initialized = true; | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | void bochs_fbdev_fini(struct bochs_device *bochs) | ||
209 | { | ||
210 | if (!bochs->fb.initialized) | ||
211 | return; | ||
212 | |||
213 | bochs_fbdev_destroy(bochs); | ||
214 | bochs->fb.initialized = false; | ||
215 | } | ||
diff --git a/drivers/gpu/drm/bochs/bochs_hw.c b/drivers/gpu/drm/bochs/bochs_hw.c new file mode 100644 index 000000000000..dbe619e6aab4 --- /dev/null +++ b/drivers/gpu/drm/bochs/bochs_hw.c | |||
@@ -0,0 +1,177 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License as published by | ||
4 | * the Free Software Foundation; either version 2 of the License, or | ||
5 | * (at your option) any later version. | ||
6 | */ | ||
7 | |||
8 | #include "bochs.h" | ||
9 | |||
10 | /* ---------------------------------------------------------------------- */ | ||
11 | |||
12 | static void bochs_vga_writeb(struct bochs_device *bochs, u16 ioport, u8 val) | ||
13 | { | ||
14 | if (WARN_ON(ioport < 0x3c0 || ioport > 0x3df)) | ||
15 | return; | ||
16 | |||
17 | if (bochs->mmio) { | ||
18 | int offset = ioport - 0x3c0 + 0x400; | ||
19 | writeb(val, bochs->mmio + offset); | ||
20 | } else { | ||
21 | outb(val, ioport); | ||
22 | } | ||
23 | } | ||
24 | |||
25 | static u16 bochs_dispi_read(struct bochs_device *bochs, u16 reg) | ||
26 | { | ||
27 | u16 ret = 0; | ||
28 | |||
29 | if (bochs->mmio) { | ||
30 | int offset = 0x500 + (reg << 1); | ||
31 | ret = readw(bochs->mmio + offset); | ||
32 | } else { | ||
33 | outw(reg, VBE_DISPI_IOPORT_INDEX); | ||
34 | ret = inw(VBE_DISPI_IOPORT_DATA); | ||
35 | } | ||
36 | return ret; | ||
37 | } | ||
38 | |||
39 | static void bochs_dispi_write(struct bochs_device *bochs, u16 reg, u16 val) | ||
40 | { | ||
41 | if (bochs->mmio) { | ||
42 | int offset = 0x500 + (reg << 1); | ||
43 | writew(val, bochs->mmio + offset); | ||
44 | } else { | ||
45 | outw(reg, VBE_DISPI_IOPORT_INDEX); | ||
46 | outw(val, VBE_DISPI_IOPORT_DATA); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | int bochs_hw_init(struct drm_device *dev, uint32_t flags) | ||
51 | { | ||
52 | struct bochs_device *bochs = dev->dev_private; | ||
53 | struct pci_dev *pdev = dev->pdev; | ||
54 | unsigned long addr, size, mem, ioaddr, iosize; | ||
55 | u16 id; | ||
56 | |||
57 | if (/* (ent->driver_data == BOCHS_QEMU_STDVGA) && */ | ||
58 | (pdev->resource[2].flags & IORESOURCE_MEM)) { | ||
59 | /* mmio bar with vga and bochs registers present */ | ||
60 | if (pci_request_region(pdev, 2, "bochs-drm") != 0) { | ||
61 | DRM_ERROR("Cannot request mmio region\n"); | ||
62 | return -EBUSY; | ||
63 | } | ||
64 | ioaddr = pci_resource_start(pdev, 2); | ||
65 | iosize = pci_resource_len(pdev, 2); | ||
66 | bochs->mmio = ioremap(ioaddr, iosize); | ||
67 | if (bochs->mmio == NULL) { | ||
68 | DRM_ERROR("Cannot map mmio region\n"); | ||
69 | return -ENOMEM; | ||
70 | } | ||
71 | } else { | ||
72 | ioaddr = VBE_DISPI_IOPORT_INDEX; | ||
73 | iosize = 2; | ||
74 | if (!request_region(ioaddr, iosize, "bochs-drm")) { | ||
75 | DRM_ERROR("Cannot request ioports\n"); | ||
76 | return -EBUSY; | ||
77 | } | ||
78 | bochs->ioports = 1; | ||
79 | } | ||
80 | |||
81 | id = bochs_dispi_read(bochs, VBE_DISPI_INDEX_ID); | ||
82 | mem = bochs_dispi_read(bochs, VBE_DISPI_INDEX_VIDEO_MEMORY_64K) | ||
83 | * 64 * 1024; | ||
84 | if ((id & 0xfff0) != VBE_DISPI_ID0) { | ||
85 | DRM_ERROR("ID mismatch\n"); | ||
86 | return -ENODEV; | ||
87 | } | ||
88 | |||
89 | if ((pdev->resource[0].flags & IORESOURCE_MEM) == 0) | ||
90 | return -ENODEV; | ||
91 | addr = pci_resource_start(pdev, 0); | ||
92 | size = pci_resource_len(pdev, 0); | ||
93 | if (addr == 0) | ||
94 | return -ENODEV; | ||
95 | if (size != mem) { | ||
96 | DRM_ERROR("Size mismatch: pci=%ld, bochs=%ld\n", | ||
97 | size, mem); | ||
98 | size = min(size, mem); | ||
99 | } | ||
100 | |||
101 | if (pci_request_region(pdev, 0, "bochs-drm") != 0) { | ||
102 | DRM_ERROR("Cannot request framebuffer\n"); | ||
103 | return -EBUSY; | ||
104 | } | ||
105 | |||
106 | bochs->fb_map = ioremap(addr, size); | ||
107 | if (bochs->fb_map == NULL) { | ||
108 | DRM_ERROR("Cannot map framebuffer\n"); | ||
109 | return -ENOMEM; | ||
110 | } | ||
111 | bochs->fb_base = addr; | ||
112 | bochs->fb_size = size; | ||
113 | |||
114 | DRM_INFO("Found bochs VGA, ID 0x%x.\n", id); | ||
115 | DRM_INFO("Framebuffer size %ld kB @ 0x%lx, %s @ 0x%lx.\n", | ||
116 | size / 1024, addr, | ||
117 | bochs->ioports ? "ioports" : "mmio", | ||
118 | ioaddr); | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | void bochs_hw_fini(struct drm_device *dev) | ||
123 | { | ||
124 | struct bochs_device *bochs = dev->dev_private; | ||
125 | |||
126 | if (bochs->mmio) | ||
127 | iounmap(bochs->mmio); | ||
128 | if (bochs->ioports) | ||
129 | release_region(VBE_DISPI_IOPORT_INDEX, 2); | ||
130 | if (bochs->fb_map) | ||
131 | iounmap(bochs->fb_map); | ||
132 | pci_release_regions(dev->pdev); | ||
133 | } | ||
134 | |||
135 | void bochs_hw_setmode(struct bochs_device *bochs, | ||
136 | struct drm_display_mode *mode) | ||
137 | { | ||
138 | bochs->xres = mode->hdisplay; | ||
139 | bochs->yres = mode->vdisplay; | ||
140 | bochs->bpp = 32; | ||
141 | bochs->stride = mode->hdisplay * (bochs->bpp / 8); | ||
142 | bochs->yres_virtual = bochs->fb_size / bochs->stride; | ||
143 | |||
144 | DRM_DEBUG_DRIVER("%dx%d @ %d bpp, vy %d\n", | ||
145 | bochs->xres, bochs->yres, bochs->bpp, | ||
146 | bochs->yres_virtual); | ||
147 | |||
148 | bochs_vga_writeb(bochs, 0x3c0, 0x20); /* unblank */ | ||
149 | |||
150 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_BPP, bochs->bpp); | ||
151 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_XRES, bochs->xres); | ||
152 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_YRES, bochs->yres); | ||
153 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_BANK, 0); | ||
154 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_WIDTH, bochs->xres); | ||
155 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_HEIGHT, | ||
156 | bochs->yres_virtual); | ||
157 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, 0); | ||
158 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, 0); | ||
159 | |||
160 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, | ||
161 | VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED); | ||
162 | } | ||
163 | |||
164 | void bochs_hw_setbase(struct bochs_device *bochs, | ||
165 | int x, int y, u64 addr) | ||
166 | { | ||
167 | unsigned long offset = (unsigned long)addr + | ||
168 | y * bochs->stride + | ||
169 | x * (bochs->bpp / 8); | ||
170 | int vy = offset / bochs->stride; | ||
171 | int vx = (offset % bochs->stride) * 8 / bochs->bpp; | ||
172 | |||
173 | DRM_DEBUG_DRIVER("x %d, y %d, addr %llx -> offset %lx, vx %d, vy %d\n", | ||
174 | x, y, addr, offset, vx, vy); | ||
175 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, vx); | ||
176 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, vy); | ||
177 | } | ||
diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c new file mode 100644 index 000000000000..62ec7d4b3816 --- /dev/null +++ b/drivers/gpu/drm/bochs/bochs_kms.c | |||
@@ -0,0 +1,294 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License as published by | ||
4 | * the Free Software Foundation; either version 2 of the License, or | ||
5 | * (at your option) any later version. | ||
6 | */ | ||
7 | |||
8 | #include "bochs.h" | ||
9 | |||
10 | static int defx = 1024; | ||
11 | static int defy = 768; | ||
12 | |||
13 | module_param(defx, int, 0444); | ||
14 | module_param(defy, int, 0444); | ||
15 | MODULE_PARM_DESC(defx, "default x resolution"); | ||
16 | MODULE_PARM_DESC(defy, "default y resolution"); | ||
17 | |||
18 | /* ---------------------------------------------------------------------- */ | ||
19 | |||
20 | static void bochs_crtc_load_lut(struct drm_crtc *crtc) | ||
21 | { | ||
22 | } | ||
23 | |||
24 | static void bochs_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
25 | { | ||
26 | switch (mode) { | ||
27 | case DRM_MODE_DPMS_ON: | ||
28 | case DRM_MODE_DPMS_STANDBY: | ||
29 | case DRM_MODE_DPMS_SUSPEND: | ||
30 | case DRM_MODE_DPMS_OFF: | ||
31 | default: | ||
32 | return; | ||
33 | } | ||
34 | } | ||
35 | |||
36 | static bool bochs_crtc_mode_fixup(struct drm_crtc *crtc, | ||
37 | const struct drm_display_mode *mode, | ||
38 | struct drm_display_mode *adjusted_mode) | ||
39 | { | ||
40 | return true; | ||
41 | } | ||
42 | |||
43 | static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | ||
44 | struct drm_framebuffer *old_fb) | ||
45 | { | ||
46 | struct bochs_device *bochs = | ||
47 | container_of(crtc, struct bochs_device, crtc); | ||
48 | struct bochs_framebuffer *bochs_fb; | ||
49 | struct bochs_bo *bo; | ||
50 | u64 gpu_addr = 0; | ||
51 | int ret; | ||
52 | |||
53 | if (old_fb) { | ||
54 | bochs_fb = to_bochs_framebuffer(old_fb); | ||
55 | bo = gem_to_bochs_bo(bochs_fb->obj); | ||
56 | ret = ttm_bo_reserve(&bo->bo, true, false, false, 0); | ||
57 | if (ret) { | ||
58 | DRM_ERROR("failed to reserve old_fb bo\n"); | ||
59 | } else { | ||
60 | bochs_bo_unpin(bo); | ||
61 | ttm_bo_unreserve(&bo->bo); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | if (WARN_ON(crtc->fb == NULL)) | ||
66 | return -EINVAL; | ||
67 | |||
68 | bochs_fb = to_bochs_framebuffer(crtc->fb); | ||
69 | bo = gem_to_bochs_bo(bochs_fb->obj); | ||
70 | ret = ttm_bo_reserve(&bo->bo, true, false, false, 0); | ||
71 | if (ret) | ||
72 | return ret; | ||
73 | |||
74 | ret = bochs_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); | ||
75 | if (ret) { | ||
76 | ttm_bo_unreserve(&bo->bo); | ||
77 | return ret; | ||
78 | } | ||
79 | |||
80 | ttm_bo_unreserve(&bo->bo); | ||
81 | bochs_hw_setbase(bochs, x, y, gpu_addr); | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | static int bochs_crtc_mode_set(struct drm_crtc *crtc, | ||
86 | struct drm_display_mode *mode, | ||
87 | struct drm_display_mode *adjusted_mode, | ||
88 | int x, int y, struct drm_framebuffer *old_fb) | ||
89 | { | ||
90 | struct bochs_device *bochs = | ||
91 | container_of(crtc, struct bochs_device, crtc); | ||
92 | |||
93 | bochs_hw_setmode(bochs, mode); | ||
94 | bochs_crtc_mode_set_base(crtc, x, y, old_fb); | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static void bochs_crtc_prepare(struct drm_crtc *crtc) | ||
99 | { | ||
100 | } | ||
101 | |||
102 | static void bochs_crtc_commit(struct drm_crtc *crtc) | ||
103 | { | ||
104 | } | ||
105 | |||
106 | static void bochs_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, | ||
107 | u16 *blue, uint32_t start, uint32_t size) | ||
108 | { | ||
109 | } | ||
110 | |||
111 | /* These provide the minimum set of functions required to handle a CRTC */ | ||
112 | static const struct drm_crtc_funcs bochs_crtc_funcs = { | ||
113 | .gamma_set = bochs_crtc_gamma_set, | ||
114 | .set_config = drm_crtc_helper_set_config, | ||
115 | .destroy = drm_crtc_cleanup, | ||
116 | }; | ||
117 | |||
118 | static const struct drm_crtc_helper_funcs bochs_helper_funcs = { | ||
119 | .dpms = bochs_crtc_dpms, | ||
120 | .mode_fixup = bochs_crtc_mode_fixup, | ||
121 | .mode_set = bochs_crtc_mode_set, | ||
122 | .mode_set_base = bochs_crtc_mode_set_base, | ||
123 | .prepare = bochs_crtc_prepare, | ||
124 | .commit = bochs_crtc_commit, | ||
125 | .load_lut = bochs_crtc_load_lut, | ||
126 | }; | ||
127 | |||
128 | static void bochs_crtc_init(struct drm_device *dev) | ||
129 | { | ||
130 | struct bochs_device *bochs = dev->dev_private; | ||
131 | struct drm_crtc *crtc = &bochs->crtc; | ||
132 | |||
133 | drm_crtc_init(dev, crtc, &bochs_crtc_funcs); | ||
134 | drm_mode_crtc_set_gamma_size(crtc, 256); | ||
135 | drm_crtc_helper_add(crtc, &bochs_helper_funcs); | ||
136 | } | ||
137 | |||
138 | static bool bochs_encoder_mode_fixup(struct drm_encoder *encoder, | ||
139 | const struct drm_display_mode *mode, | ||
140 | struct drm_display_mode *adjusted_mode) | ||
141 | { | ||
142 | return true; | ||
143 | } | ||
144 | |||
145 | static void bochs_encoder_mode_set(struct drm_encoder *encoder, | ||
146 | struct drm_display_mode *mode, | ||
147 | struct drm_display_mode *adjusted_mode) | ||
148 | { | ||
149 | } | ||
150 | |||
151 | static void bochs_encoder_dpms(struct drm_encoder *encoder, int state) | ||
152 | { | ||
153 | } | ||
154 | |||
155 | static void bochs_encoder_prepare(struct drm_encoder *encoder) | ||
156 | { | ||
157 | } | ||
158 | |||
159 | static void bochs_encoder_commit(struct drm_encoder *encoder) | ||
160 | { | ||
161 | } | ||
162 | |||
163 | static const struct drm_encoder_helper_funcs bochs_encoder_helper_funcs = { | ||
164 | .dpms = bochs_encoder_dpms, | ||
165 | .mode_fixup = bochs_encoder_mode_fixup, | ||
166 | .mode_set = bochs_encoder_mode_set, | ||
167 | .prepare = bochs_encoder_prepare, | ||
168 | .commit = bochs_encoder_commit, | ||
169 | }; | ||
170 | |||
171 | static const struct drm_encoder_funcs bochs_encoder_encoder_funcs = { | ||
172 | .destroy = drm_encoder_cleanup, | ||
173 | }; | ||
174 | |||
175 | static void bochs_encoder_init(struct drm_device *dev) | ||
176 | { | ||
177 | struct bochs_device *bochs = dev->dev_private; | ||
178 | struct drm_encoder *encoder = &bochs->encoder; | ||
179 | |||
180 | encoder->possible_crtcs = 0x1; | ||
181 | drm_encoder_init(dev, encoder, &bochs_encoder_encoder_funcs, | ||
182 | DRM_MODE_ENCODER_DAC); | ||
183 | drm_encoder_helper_add(encoder, &bochs_encoder_helper_funcs); | ||
184 | } | ||
185 | |||
186 | |||
187 | int bochs_connector_get_modes(struct drm_connector *connector) | ||
188 | { | ||
189 | int count; | ||
190 | |||
191 | count = drm_add_modes_noedid(connector, 8192, 8192); | ||
192 | drm_set_preferred_mode(connector, defx, defy); | ||
193 | return count; | ||
194 | } | ||
195 | |||
196 | static int bochs_connector_mode_valid(struct drm_connector *connector, | ||
197 | struct drm_display_mode *mode) | ||
198 | { | ||
199 | struct bochs_device *bochs = | ||
200 | container_of(connector, struct bochs_device, connector); | ||
201 | unsigned long size = mode->hdisplay * mode->vdisplay * 4; | ||
202 | |||
203 | /* | ||
204 | * Make sure we can fit two framebuffers into video memory. | ||
205 | * This allows up to 1600x1200 with 16 MB (default size). | ||
206 | * If you want more try this: | ||
207 | * 'qemu -vga std -global VGA.vgamem_mb=32 $otherargs' | ||
208 | */ | ||
209 | if (size * 2 > bochs->fb_size) | ||
210 | return MODE_BAD; | ||
211 | |||
212 | return MODE_OK; | ||
213 | } | ||
214 | |||
215 | static struct drm_encoder * | ||
216 | bochs_connector_best_encoder(struct drm_connector *connector) | ||
217 | { | ||
218 | int enc_id = connector->encoder_ids[0]; | ||
219 | struct drm_mode_object *obj; | ||
220 | struct drm_encoder *encoder; | ||
221 | |||
222 | /* pick the encoder ids */ | ||
223 | if (enc_id) { | ||
224 | obj = drm_mode_object_find(connector->dev, enc_id, | ||
225 | DRM_MODE_OBJECT_ENCODER); | ||
226 | if (!obj) | ||
227 | return NULL; | ||
228 | encoder = obj_to_encoder(obj); | ||
229 | return encoder; | ||
230 | } | ||
231 | return NULL; | ||
232 | } | ||
233 | |||
234 | static enum drm_connector_status bochs_connector_detect(struct drm_connector | ||
235 | *connector, bool force) | ||
236 | { | ||
237 | return connector_status_connected; | ||
238 | } | ||
239 | |||
240 | struct drm_connector_helper_funcs bochs_connector_connector_helper_funcs = { | ||
241 | .get_modes = bochs_connector_get_modes, | ||
242 | .mode_valid = bochs_connector_mode_valid, | ||
243 | .best_encoder = bochs_connector_best_encoder, | ||
244 | }; | ||
245 | |||
246 | struct drm_connector_funcs bochs_connector_connector_funcs = { | ||
247 | .dpms = drm_helper_connector_dpms, | ||
248 | .detect = bochs_connector_detect, | ||
249 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
250 | .destroy = drm_connector_cleanup, | ||
251 | }; | ||
252 | |||
253 | static void bochs_connector_init(struct drm_device *dev) | ||
254 | { | ||
255 | struct bochs_device *bochs = dev->dev_private; | ||
256 | struct drm_connector *connector = &bochs->connector; | ||
257 | |||
258 | drm_connector_init(dev, connector, &bochs_connector_connector_funcs, | ||
259 | DRM_MODE_CONNECTOR_VIRTUAL); | ||
260 | drm_connector_helper_add(connector, | ||
261 | &bochs_connector_connector_helper_funcs); | ||
262 | } | ||
263 | |||
264 | |||
265 | int bochs_kms_init(struct bochs_device *bochs) | ||
266 | { | ||
267 | drm_mode_config_init(bochs->dev); | ||
268 | bochs->mode_config_initialized = true; | ||
269 | |||
270 | bochs->dev->mode_config.max_width = 8192; | ||
271 | bochs->dev->mode_config.max_height = 8192; | ||
272 | |||
273 | bochs->dev->mode_config.fb_base = bochs->fb_base; | ||
274 | bochs->dev->mode_config.preferred_depth = 24; | ||
275 | bochs->dev->mode_config.prefer_shadow = 0; | ||
276 | |||
277 | bochs->dev->mode_config.funcs = (void *)&bochs_mode_funcs; | ||
278 | |||
279 | bochs_crtc_init(bochs->dev); | ||
280 | bochs_encoder_init(bochs->dev); | ||
281 | bochs_connector_init(bochs->dev); | ||
282 | drm_mode_connector_attach_encoder(&bochs->connector, | ||
283 | &bochs->encoder); | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | void bochs_kms_fini(struct bochs_device *bochs) | ||
289 | { | ||
290 | if (bochs->mode_config_initialized) { | ||
291 | drm_mode_config_cleanup(bochs->dev); | ||
292 | bochs->mode_config_initialized = false; | ||
293 | } | ||
294 | } | ||
diff --git a/drivers/gpu/drm/bochs/bochs_mm.c b/drivers/gpu/drm/bochs/bochs_mm.c new file mode 100644 index 000000000000..ce6858765b37 --- /dev/null +++ b/drivers/gpu/drm/bochs/bochs_mm.c | |||
@@ -0,0 +1,546 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License as published by | ||
4 | * the Free Software Foundation; either version 2 of the License, or | ||
5 | * (at your option) any later version. | ||
6 | */ | ||
7 | |||
8 | #include "bochs.h" | ||
9 | |||
10 | static void bochs_ttm_placement(struct bochs_bo *bo, int domain); | ||
11 | |||
12 | /* ---------------------------------------------------------------------- */ | ||
13 | |||
14 | static inline struct bochs_device *bochs_bdev(struct ttm_bo_device *bd) | ||
15 | { | ||
16 | return container_of(bd, struct bochs_device, ttm.bdev); | ||
17 | } | ||
18 | |||
19 | static int bochs_ttm_mem_global_init(struct drm_global_reference *ref) | ||
20 | { | ||
21 | return ttm_mem_global_init(ref->object); | ||
22 | } | ||
23 | |||
24 | static void bochs_ttm_mem_global_release(struct drm_global_reference *ref) | ||
25 | { | ||
26 | ttm_mem_global_release(ref->object); | ||
27 | } | ||
28 | |||
29 | static int bochs_ttm_global_init(struct bochs_device *bochs) | ||
30 | { | ||
31 | struct drm_global_reference *global_ref; | ||
32 | int r; | ||
33 | |||
34 | global_ref = &bochs->ttm.mem_global_ref; | ||
35 | global_ref->global_type = DRM_GLOBAL_TTM_MEM; | ||
36 | global_ref->size = sizeof(struct ttm_mem_global); | ||
37 | global_ref->init = &bochs_ttm_mem_global_init; | ||
38 | global_ref->release = &bochs_ttm_mem_global_release; | ||
39 | r = drm_global_item_ref(global_ref); | ||
40 | if (r != 0) { | ||
41 | DRM_ERROR("Failed setting up TTM memory accounting " | ||
42 | "subsystem.\n"); | ||
43 | return r; | ||
44 | } | ||
45 | |||
46 | bochs->ttm.bo_global_ref.mem_glob = | ||
47 | bochs->ttm.mem_global_ref.object; | ||
48 | global_ref = &bochs->ttm.bo_global_ref.ref; | ||
49 | global_ref->global_type = DRM_GLOBAL_TTM_BO; | ||
50 | global_ref->size = sizeof(struct ttm_bo_global); | ||
51 | global_ref->init = &ttm_bo_global_init; | ||
52 | global_ref->release = &ttm_bo_global_release; | ||
53 | r = drm_global_item_ref(global_ref); | ||
54 | if (r != 0) { | ||
55 | DRM_ERROR("Failed setting up TTM BO subsystem.\n"); | ||
56 | drm_global_item_unref(&bochs->ttm.mem_global_ref); | ||
57 | return r; | ||
58 | } | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | static void bochs_ttm_global_release(struct bochs_device *bochs) | ||
64 | { | ||
65 | if (bochs->ttm.mem_global_ref.release == NULL) | ||
66 | return; | ||
67 | |||
68 | drm_global_item_unref(&bochs->ttm.bo_global_ref.ref); | ||
69 | drm_global_item_unref(&bochs->ttm.mem_global_ref); | ||
70 | bochs->ttm.mem_global_ref.release = NULL; | ||
71 | } | ||
72 | |||
73 | |||
74 | static void bochs_bo_ttm_destroy(struct ttm_buffer_object *tbo) | ||
75 | { | ||
76 | struct bochs_bo *bo; | ||
77 | |||
78 | bo = container_of(tbo, struct bochs_bo, bo); | ||
79 | drm_gem_object_release(&bo->gem); | ||
80 | kfree(bo); | ||
81 | } | ||
82 | |||
83 | static bool bochs_ttm_bo_is_bochs_bo(struct ttm_buffer_object *bo) | ||
84 | { | ||
85 | if (bo->destroy == &bochs_bo_ttm_destroy) | ||
86 | return true; | ||
87 | return false; | ||
88 | } | ||
89 | |||
90 | static int bochs_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, | ||
91 | struct ttm_mem_type_manager *man) | ||
92 | { | ||
93 | switch (type) { | ||
94 | case TTM_PL_SYSTEM: | ||
95 | man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; | ||
96 | man->available_caching = TTM_PL_MASK_CACHING; | ||
97 | man->default_caching = TTM_PL_FLAG_CACHED; | ||
98 | break; | ||
99 | case TTM_PL_VRAM: | ||
100 | man->func = &ttm_bo_manager_func; | ||
101 | man->flags = TTM_MEMTYPE_FLAG_FIXED | | ||
102 | TTM_MEMTYPE_FLAG_MAPPABLE; | ||
103 | man->available_caching = TTM_PL_FLAG_UNCACHED | | ||
104 | TTM_PL_FLAG_WC; | ||
105 | man->default_caching = TTM_PL_FLAG_WC; | ||
106 | break; | ||
107 | default: | ||
108 | DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); | ||
109 | return -EINVAL; | ||
110 | } | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static void | ||
115 | bochs_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) | ||
116 | { | ||
117 | struct bochs_bo *bochsbo = bochs_bo(bo); | ||
118 | |||
119 | if (!bochs_ttm_bo_is_bochs_bo(bo)) | ||
120 | return; | ||
121 | |||
122 | bochs_ttm_placement(bochsbo, TTM_PL_FLAG_SYSTEM); | ||
123 | *pl = bochsbo->placement; | ||
124 | } | ||
125 | |||
126 | static int bochs_bo_verify_access(struct ttm_buffer_object *bo, | ||
127 | struct file *filp) | ||
128 | { | ||
129 | struct bochs_bo *bochsbo = bochs_bo(bo); | ||
130 | |||
131 | return drm_vma_node_verify_access(&bochsbo->gem.vma_node, filp); | ||
132 | } | ||
133 | |||
134 | static int bochs_ttm_io_mem_reserve(struct ttm_bo_device *bdev, | ||
135 | struct ttm_mem_reg *mem) | ||
136 | { | ||
137 | struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; | ||
138 | struct bochs_device *bochs = bochs_bdev(bdev); | ||
139 | |||
140 | mem->bus.addr = NULL; | ||
141 | mem->bus.offset = 0; | ||
142 | mem->bus.size = mem->num_pages << PAGE_SHIFT; | ||
143 | mem->bus.base = 0; | ||
144 | mem->bus.is_iomem = false; | ||
145 | if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) | ||
146 | return -EINVAL; | ||
147 | switch (mem->mem_type) { | ||
148 | case TTM_PL_SYSTEM: | ||
149 | /* system memory */ | ||
150 | return 0; | ||
151 | case TTM_PL_VRAM: | ||
152 | mem->bus.offset = mem->start << PAGE_SHIFT; | ||
153 | mem->bus.base = bochs->fb_base; | ||
154 | mem->bus.is_iomem = true; | ||
155 | break; | ||
156 | default: | ||
157 | return -EINVAL; | ||
158 | break; | ||
159 | } | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static void bochs_ttm_io_mem_free(struct ttm_bo_device *bdev, | ||
164 | struct ttm_mem_reg *mem) | ||
165 | { | ||
166 | } | ||
167 | |||
168 | static int bochs_bo_move(struct ttm_buffer_object *bo, | ||
169 | bool evict, bool interruptible, | ||
170 | bool no_wait_gpu, | ||
171 | struct ttm_mem_reg *new_mem) | ||
172 | { | ||
173 | return ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); | ||
174 | } | ||
175 | |||
176 | |||
177 | static void bochs_ttm_backend_destroy(struct ttm_tt *tt) | ||
178 | { | ||
179 | ttm_tt_fini(tt); | ||
180 | kfree(tt); | ||
181 | } | ||
182 | |||
183 | static struct ttm_backend_func bochs_tt_backend_func = { | ||
184 | .destroy = &bochs_ttm_backend_destroy, | ||
185 | }; | ||
186 | |||
187 | static struct ttm_tt *bochs_ttm_tt_create(struct ttm_bo_device *bdev, | ||
188 | unsigned long size, | ||
189 | uint32_t page_flags, | ||
190 | struct page *dummy_read_page) | ||
191 | { | ||
192 | struct ttm_tt *tt; | ||
193 | |||
194 | tt = kzalloc(sizeof(struct ttm_tt), GFP_KERNEL); | ||
195 | if (tt == NULL) | ||
196 | return NULL; | ||
197 | tt->func = &bochs_tt_backend_func; | ||
198 | if (ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page)) { | ||
199 | kfree(tt); | ||
200 | return NULL; | ||
201 | } | ||
202 | return tt; | ||
203 | } | ||
204 | |||
205 | struct ttm_bo_driver bochs_bo_driver = { | ||
206 | .ttm_tt_create = bochs_ttm_tt_create, | ||
207 | .ttm_tt_populate = ttm_pool_populate, | ||
208 | .ttm_tt_unpopulate = ttm_pool_unpopulate, | ||
209 | .init_mem_type = bochs_bo_init_mem_type, | ||
210 | .evict_flags = bochs_bo_evict_flags, | ||
211 | .move = bochs_bo_move, | ||
212 | .verify_access = bochs_bo_verify_access, | ||
213 | .io_mem_reserve = &bochs_ttm_io_mem_reserve, | ||
214 | .io_mem_free = &bochs_ttm_io_mem_free, | ||
215 | }; | ||
216 | |||
217 | int bochs_mm_init(struct bochs_device *bochs) | ||
218 | { | ||
219 | struct ttm_bo_device *bdev = &bochs->ttm.bdev; | ||
220 | int ret; | ||
221 | |||
222 | ret = bochs_ttm_global_init(bochs); | ||
223 | if (ret) | ||
224 | return ret; | ||
225 | |||
226 | ret = ttm_bo_device_init(&bochs->ttm.bdev, | ||
227 | bochs->ttm.bo_global_ref.ref.object, | ||
228 | &bochs_bo_driver, DRM_FILE_PAGE_OFFSET, | ||
229 | true); | ||
230 | if (ret) { | ||
231 | DRM_ERROR("Error initialising bo driver; %d\n", ret); | ||
232 | return ret; | ||
233 | } | ||
234 | |||
235 | ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM, | ||
236 | bochs->fb_size >> PAGE_SHIFT); | ||
237 | if (ret) { | ||
238 | DRM_ERROR("Failed ttm VRAM init: %d\n", ret); | ||
239 | return ret; | ||
240 | } | ||
241 | |||
242 | bochs->ttm.initialized = true; | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | void bochs_mm_fini(struct bochs_device *bochs) | ||
247 | { | ||
248 | if (!bochs->ttm.initialized) | ||
249 | return; | ||
250 | |||
251 | ttm_bo_device_release(&bochs->ttm.bdev); | ||
252 | bochs_ttm_global_release(bochs); | ||
253 | bochs->ttm.initialized = false; | ||
254 | } | ||
255 | |||
256 | static void bochs_ttm_placement(struct bochs_bo *bo, int domain) | ||
257 | { | ||
258 | u32 c = 0; | ||
259 | bo->placement.fpfn = 0; | ||
260 | bo->placement.lpfn = 0; | ||
261 | bo->placement.placement = bo->placements; | ||
262 | bo->placement.busy_placement = bo->placements; | ||
263 | if (domain & TTM_PL_FLAG_VRAM) { | ||
264 | bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | ||
265 | | TTM_PL_FLAG_VRAM; | ||
266 | } | ||
267 | if (domain & TTM_PL_FLAG_SYSTEM) { | ||
268 | bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; | ||
269 | } | ||
270 | if (!c) { | ||
271 | bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; | ||
272 | } | ||
273 | bo->placement.num_placement = c; | ||
274 | bo->placement.num_busy_placement = c; | ||
275 | } | ||
276 | |||
277 | static inline u64 bochs_bo_gpu_offset(struct bochs_bo *bo) | ||
278 | { | ||
279 | return bo->bo.offset; | ||
280 | } | ||
281 | |||
282 | int bochs_bo_pin(struct bochs_bo *bo, u32 pl_flag, u64 *gpu_addr) | ||
283 | { | ||
284 | int i, ret; | ||
285 | |||
286 | if (bo->pin_count) { | ||
287 | bo->pin_count++; | ||
288 | if (gpu_addr) | ||
289 | *gpu_addr = bochs_bo_gpu_offset(bo); | ||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | bochs_ttm_placement(bo, pl_flag); | ||
294 | for (i = 0; i < bo->placement.num_placement; i++) | ||
295 | bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; | ||
296 | ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); | ||
297 | if (ret) | ||
298 | return ret; | ||
299 | |||
300 | bo->pin_count = 1; | ||
301 | if (gpu_addr) | ||
302 | *gpu_addr = bochs_bo_gpu_offset(bo); | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | int bochs_bo_unpin(struct bochs_bo *bo) | ||
307 | { | ||
308 | int i, ret; | ||
309 | |||
310 | if (!bo->pin_count) { | ||
311 | DRM_ERROR("unpin bad %p\n", bo); | ||
312 | return 0; | ||
313 | } | ||
314 | bo->pin_count--; | ||
315 | |||
316 | if (bo->pin_count) | ||
317 | return 0; | ||
318 | |||
319 | for (i = 0; i < bo->placement.num_placement; i++) | ||
320 | bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; | ||
321 | ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); | ||
322 | if (ret) | ||
323 | return ret; | ||
324 | |||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | int bochs_mmap(struct file *filp, struct vm_area_struct *vma) | ||
329 | { | ||
330 | struct drm_file *file_priv; | ||
331 | struct bochs_device *bochs; | ||
332 | |||
333 | if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) | ||
334 | return drm_mmap(filp, vma); | ||
335 | |||
336 | file_priv = filp->private_data; | ||
337 | bochs = file_priv->minor->dev->dev_private; | ||
338 | return ttm_bo_mmap(filp, vma, &bochs->ttm.bdev); | ||
339 | } | ||
340 | |||
341 | /* ---------------------------------------------------------------------- */ | ||
342 | |||
343 | static int bochs_bo_create(struct drm_device *dev, int size, int align, | ||
344 | uint32_t flags, struct bochs_bo **pbochsbo) | ||
345 | { | ||
346 | struct bochs_device *bochs = dev->dev_private; | ||
347 | struct bochs_bo *bochsbo; | ||
348 | size_t acc_size; | ||
349 | int ret; | ||
350 | |||
351 | bochsbo = kzalloc(sizeof(struct bochs_bo), GFP_KERNEL); | ||
352 | if (!bochsbo) | ||
353 | return -ENOMEM; | ||
354 | |||
355 | ret = drm_gem_object_init(dev, &bochsbo->gem, size); | ||
356 | if (ret) { | ||
357 | kfree(bochsbo); | ||
358 | return ret; | ||
359 | } | ||
360 | |||
361 | bochsbo->bo.bdev = &bochs->ttm.bdev; | ||
362 | bochsbo->bo.bdev->dev_mapping = dev->dev_mapping; | ||
363 | |||
364 | bochs_ttm_placement(bochsbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); | ||
365 | |||
366 | acc_size = ttm_bo_dma_acc_size(&bochs->ttm.bdev, size, | ||
367 | sizeof(struct bochs_bo)); | ||
368 | |||
369 | ret = ttm_bo_init(&bochs->ttm.bdev, &bochsbo->bo, size, | ||
370 | ttm_bo_type_device, &bochsbo->placement, | ||
371 | align >> PAGE_SHIFT, false, NULL, acc_size, | ||
372 | NULL, bochs_bo_ttm_destroy); | ||
373 | if (ret) | ||
374 | return ret; | ||
375 | |||
376 | *pbochsbo = bochsbo; | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | int bochs_gem_create(struct drm_device *dev, u32 size, bool iskernel, | ||
381 | struct drm_gem_object **obj) | ||
382 | { | ||
383 | struct bochs_bo *bochsbo; | ||
384 | int ret; | ||
385 | |||
386 | *obj = NULL; | ||
387 | |||
388 | size = ALIGN(size, PAGE_SIZE); | ||
389 | if (size == 0) | ||
390 | return -EINVAL; | ||
391 | |||
392 | ret = bochs_bo_create(dev, size, 0, 0, &bochsbo); | ||
393 | if (ret) { | ||
394 | if (ret != -ERESTARTSYS) | ||
395 | DRM_ERROR("failed to allocate GEM object\n"); | ||
396 | return ret; | ||
397 | } | ||
398 | *obj = &bochsbo->gem; | ||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | int bochs_dumb_create(struct drm_file *file, struct drm_device *dev, | ||
403 | struct drm_mode_create_dumb *args) | ||
404 | { | ||
405 | struct drm_gem_object *gobj; | ||
406 | u32 handle; | ||
407 | int ret; | ||
408 | |||
409 | args->pitch = args->width * ((args->bpp + 7) / 8); | ||
410 | args->size = args->pitch * args->height; | ||
411 | |||
412 | ret = bochs_gem_create(dev, args->size, false, | ||
413 | &gobj); | ||
414 | if (ret) | ||
415 | return ret; | ||
416 | |||
417 | ret = drm_gem_handle_create(file, gobj, &handle); | ||
418 | drm_gem_object_unreference_unlocked(gobj); | ||
419 | if (ret) | ||
420 | return ret; | ||
421 | |||
422 | args->handle = handle; | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static void bochs_bo_unref(struct bochs_bo **bo) | ||
427 | { | ||
428 | struct ttm_buffer_object *tbo; | ||
429 | |||
430 | if ((*bo) == NULL) | ||
431 | return; | ||
432 | |||
433 | tbo = &((*bo)->bo); | ||
434 | ttm_bo_unref(&tbo); | ||
435 | if (tbo == NULL) | ||
436 | *bo = NULL; | ||
437 | |||
438 | } | ||
439 | |||
440 | void bochs_gem_free_object(struct drm_gem_object *obj) | ||
441 | { | ||
442 | struct bochs_bo *bochs_bo = gem_to_bochs_bo(obj); | ||
443 | |||
444 | if (!bochs_bo) | ||
445 | return; | ||
446 | bochs_bo_unref(&bochs_bo); | ||
447 | } | ||
448 | |||
449 | int bochs_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev, | ||
450 | uint32_t handle, uint64_t *offset) | ||
451 | { | ||
452 | struct drm_gem_object *obj; | ||
453 | int ret; | ||
454 | struct bochs_bo *bo; | ||
455 | |||
456 | mutex_lock(&dev->struct_mutex); | ||
457 | obj = drm_gem_object_lookup(dev, file, handle); | ||
458 | if (obj == NULL) { | ||
459 | ret = -ENOENT; | ||
460 | goto out_unlock; | ||
461 | } | ||
462 | |||
463 | bo = gem_to_bochs_bo(obj); | ||
464 | *offset = bochs_bo_mmap_offset(bo); | ||
465 | |||
466 | drm_gem_object_unreference(obj); | ||
467 | ret = 0; | ||
468 | out_unlock: | ||
469 | mutex_unlock(&dev->struct_mutex); | ||
470 | return ret; | ||
471 | |||
472 | } | ||
473 | |||
474 | /* ---------------------------------------------------------------------- */ | ||
475 | |||
476 | static void bochs_user_framebuffer_destroy(struct drm_framebuffer *fb) | ||
477 | { | ||
478 | struct bochs_framebuffer *bochs_fb = to_bochs_framebuffer(fb); | ||
479 | if (bochs_fb->obj) | ||
480 | drm_gem_object_unreference_unlocked(bochs_fb->obj); | ||
481 | drm_framebuffer_cleanup(fb); | ||
482 | kfree(fb); | ||
483 | } | ||
484 | |||
485 | static const struct drm_framebuffer_funcs bochs_fb_funcs = { | ||
486 | .destroy = bochs_user_framebuffer_destroy, | ||
487 | }; | ||
488 | |||
489 | int bochs_framebuffer_init(struct drm_device *dev, | ||
490 | struct bochs_framebuffer *gfb, | ||
491 | struct drm_mode_fb_cmd2 *mode_cmd, | ||
492 | struct drm_gem_object *obj) | ||
493 | { | ||
494 | int ret; | ||
495 | |||
496 | drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); | ||
497 | gfb->obj = obj; | ||
498 | ret = drm_framebuffer_init(dev, &gfb->base, &bochs_fb_funcs); | ||
499 | if (ret) { | ||
500 | DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); | ||
501 | return ret; | ||
502 | } | ||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static struct drm_framebuffer * | ||
507 | bochs_user_framebuffer_create(struct drm_device *dev, | ||
508 | struct drm_file *filp, | ||
509 | struct drm_mode_fb_cmd2 *mode_cmd) | ||
510 | { | ||
511 | struct drm_gem_object *obj; | ||
512 | struct bochs_framebuffer *bochs_fb; | ||
513 | int ret; | ||
514 | |||
515 | DRM_DEBUG_DRIVER("%dx%d, format %c%c%c%c\n", | ||
516 | mode_cmd->width, mode_cmd->height, | ||
517 | (mode_cmd->pixel_format) & 0xff, | ||
518 | (mode_cmd->pixel_format >> 8) & 0xff, | ||
519 | (mode_cmd->pixel_format >> 16) & 0xff, | ||
520 | (mode_cmd->pixel_format >> 24) & 0xff); | ||
521 | |||
522 | if (mode_cmd->pixel_format != DRM_FORMAT_XRGB8888) | ||
523 | return ERR_PTR(-ENOENT); | ||
524 | |||
525 | obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]); | ||
526 | if (obj == NULL) | ||
527 | return ERR_PTR(-ENOENT); | ||
528 | |||
529 | bochs_fb = kzalloc(sizeof(*bochs_fb), GFP_KERNEL); | ||
530 | if (!bochs_fb) { | ||
531 | drm_gem_object_unreference_unlocked(obj); | ||
532 | return ERR_PTR(-ENOMEM); | ||
533 | } | ||
534 | |||
535 | ret = bochs_framebuffer_init(dev, bochs_fb, mode_cmd, obj); | ||
536 | if (ret) { | ||
537 | drm_gem_object_unreference_unlocked(obj); | ||
538 | kfree(bochs_fb); | ||
539 | return ERR_PTR(ret); | ||
540 | } | ||
541 | return &bochs_fb->base; | ||
542 | } | ||
543 | |||
544 | const struct drm_mode_config_funcs bochs_mode_funcs = { | ||
545 | .fb_create = bochs_user_framebuffer_create, | ||
546 | }; | ||