1 // SPDX-License-Identifier: GPL-2.0-only
2
3 #include <linux/aperture.h>
4 #include <linux/ioport.h>
5 #include <linux/limits.h>
6 #include <linux/platform_device.h>
7 #include <linux/sysfb.h>
8 #include <linux/pm.h>
9
10 #include <drm/clients/drm_client_setup.h>
11 #include <drm/drm_atomic.h>
12 #include <drm/drm_atomic_state_helper.h>
13 #include <drm/drm_color_mgmt.h>
14 #include <drm/drm_connector.h>
15 #include <drm/drm_damage_helper.h>
16 #include <drm/drm_device.h>
17 #include <drm/drm_drv.h>
18 #include <drm/drm_edid.h>
19 #include <drm/drm_fbdev_shmem.h>
20 #include <drm/drm_framebuffer.h>
21 #include <drm/drm_gem_atomic_helper.h>
22 #include <drm/drm_gem_framebuffer_helper.h>
23 #include <drm/drm_gem_shmem_helper.h>
24 #include <drm/drm_managed.h>
25 #include <drm/drm_modeset_helper.h>
26 #include <drm/drm_modeset_helper_vtables.h>
27 #include <drm/drm_print.h>
28 #include <drm/drm_probe_helper.h>
29
30 #include <video/pixel_format.h>
31 #include <video/vga.h>
32
33 #include "drm_sysfb_helper.h"
34
35 #define DRIVER_NAME "vesadrm"
36 #define DRIVER_DESC "DRM driver for VESA platform devices"
37 #define DRIVER_MAJOR 1
38 #define DRIVER_MINOR 0
39
40 #define VESADRM_GAMMA_LUT_SIZE 256
41
vesadrm_get_format_si(struct drm_device * dev,const struct screen_info * si)42 static const struct drm_format_info *vesadrm_get_format_si(struct drm_device *dev,
43 const struct screen_info *si)
44 {
45 static const struct drm_sysfb_format formats[] = {
46 { PIXEL_FORMAT_XRGB1555, DRM_FORMAT_XRGB1555, },
47 { PIXEL_FORMAT_RGB565, DRM_FORMAT_RGB565, },
48 { PIXEL_FORMAT_RGB888, DRM_FORMAT_RGB888, },
49 { PIXEL_FORMAT_XRGB8888, DRM_FORMAT_XRGB8888, },
50 { PIXEL_FORMAT_XBGR8888, DRM_FORMAT_XBGR8888, },
51 { PIXEL_FORMAT_C8, DRM_FORMAT_C8, },
52 };
53 struct pixel_format pixel;
54 int ret;
55
56 ret = screen_info_pixel_format(si, &pixel);
57 if (ret)
58 return NULL;
59
60 return drm_sysfb_get_format(dev, formats, ARRAY_SIZE(formats), &pixel);
61 }
62
63 /*
64 * VESA device
65 */
66
67 struct vesadrm_device {
68 struct drm_sysfb_device sysfb;
69
70 #if defined(CONFIG_X86_32)
71 /* VESA Protected Mode interface */
72 struct {
73 const u8 *PrimaryPalette;
74 } pmi;
75 #endif
76
77 void (*cmap_write)(struct vesadrm_device *vesa, unsigned int index,
78 u16 red, u16 green, u16 blue);
79
80 /* modesetting */
81 u32 formats[DRM_SYSFB_PLANE_NFORMATS(1)];
82 struct drm_plane primary_plane;
83 struct drm_crtc crtc;
84 struct drm_encoder encoder;
85 struct drm_connector connector;
86 };
87
to_vesadrm_device(struct drm_device * dev)88 static struct vesadrm_device *to_vesadrm_device(struct drm_device *dev)
89 {
90 return container_of(to_drm_sysfb_device(dev), struct vesadrm_device, sysfb);
91 }
92
93 /*
94 * Color LUT
95 */
96
vesadrm_vga_cmap_write(struct vesadrm_device * vesa,unsigned int index,u16 red,u16 green,u16 blue)97 static void vesadrm_vga_cmap_write(struct vesadrm_device *vesa, unsigned int index,
98 u16 red, u16 green, u16 blue)
99 {
100 u8 i8 = index;
101 u8 r8 = red >> 8;
102 u8 g8 = green >> 8;
103 u8 b8 = blue >> 8;
104
105 outb_p(i8, VGA_PEL_IW);
106 outb_p(r8, VGA_PEL_D);
107 outb_p(g8, VGA_PEL_D);
108 outb_p(b8, VGA_PEL_D);
109 }
110
111 #if defined(CONFIG_X86_32)
vesadrm_pmi_cmap_write(struct vesadrm_device * vesa,unsigned int index,u16 red,u16 green,u16 blue)112 static void vesadrm_pmi_cmap_write(struct vesadrm_device *vesa, unsigned int index,
113 u16 red, u16 green, u16 blue)
114 {
115 u32 i32 = index;
116 struct {
117 u8 b8;
118 u8 g8;
119 u8 r8;
120 u8 x8;
121 } PaletteEntry = {
122 blue >> 8,
123 green >> 8,
124 red >> 8,
125 0x00,
126 };
127
128 __asm__ __volatile__ (
129 "call *(%%esi)"
130 : /* no return value */
131 : "a" (0x4f09),
132 "b" (0),
133 "c" (1),
134 "d" (i32),
135 "D" (&PaletteEntry),
136 "S" (&vesa->pmi.PrimaryPalette));
137 }
138 #endif
139
vesadrm_set_color_lut(struct drm_crtc * crtc,unsigned int index,u16 red,u16 green,u16 blue)140 static void vesadrm_set_color_lut(struct drm_crtc *crtc, unsigned int index,
141 u16 red, u16 green, u16 blue)
142 {
143 struct drm_device *dev = crtc->dev;
144 struct vesadrm_device *vesa = to_vesadrm_device(dev);
145 u8 i8 = index & 0xff;
146
147 if (drm_WARN_ON_ONCE(dev, index != i8))
148 return; /* driver bug */
149
150 vesa->cmap_write(vesa, i8, red, green, blue);
151 }
152
vesadrm_fill_gamma_lut(struct vesadrm_device * vesa,const struct drm_format_info * format)153 static void vesadrm_fill_gamma_lut(struct vesadrm_device *vesa,
154 const struct drm_format_info *format)
155 {
156 struct drm_device *dev = &vesa->sysfb.dev;
157 struct drm_crtc *crtc = &vesa->crtc;
158
159 switch (format->format) {
160 case DRM_FORMAT_XRGB1555:
161 drm_crtc_fill_gamma_555(crtc, vesadrm_set_color_lut);
162 break;
163 case DRM_FORMAT_RGB565:
164 drm_crtc_fill_gamma_565(crtc, vesadrm_set_color_lut);
165 break;
166 case DRM_FORMAT_RGB888:
167 case DRM_FORMAT_XRGB8888:
168 case DRM_FORMAT_BGRX8888:
169 drm_crtc_fill_gamma_888(crtc, vesadrm_set_color_lut);
170 break;
171 default:
172 drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n",
173 &format->format);
174 break;
175 }
176 }
177
vesadrm_load_gamma_lut(struct vesadrm_device * vesa,const struct drm_format_info * format,struct drm_color_lut * lut)178 static void vesadrm_load_gamma_lut(struct vesadrm_device *vesa,
179 const struct drm_format_info *format,
180 struct drm_color_lut *lut)
181 {
182 struct drm_device *dev = &vesa->sysfb.dev;
183 struct drm_crtc *crtc = &vesa->crtc;
184
185 switch (format->format) {
186 case DRM_FORMAT_XRGB1555:
187 drm_crtc_load_gamma_555_from_888(crtc, lut, vesadrm_set_color_lut);
188 break;
189 case DRM_FORMAT_RGB565:
190 drm_crtc_load_gamma_565_from_888(crtc, lut, vesadrm_set_color_lut);
191 break;
192 case DRM_FORMAT_RGB888:
193 case DRM_FORMAT_XRGB8888:
194 case DRM_FORMAT_BGRX8888:
195 drm_crtc_load_gamma_888(crtc, lut, vesadrm_set_color_lut);
196 break;
197 default:
198 drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n",
199 &format->format);
200 break;
201 }
202 }
203
vesadrm_fill_palette_lut(struct vesadrm_device * vesa,const struct drm_format_info * format)204 static void vesadrm_fill_palette_lut(struct vesadrm_device *vesa,
205 const struct drm_format_info *format)
206 {
207 struct drm_device *dev = &vesa->sysfb.dev;
208 struct drm_crtc *crtc = &vesa->crtc;
209
210 switch (format->format) {
211 case DRM_FORMAT_C8:
212 drm_crtc_fill_palette_8(crtc, vesadrm_set_color_lut);
213 break;
214 case DRM_FORMAT_RGB332:
215 drm_crtc_fill_palette_332(crtc, vesadrm_set_color_lut);
216 break;
217 default:
218 drm_warn_once(dev, "Unsupported format %p4cc for palette\n",
219 &format->format);
220 break;
221 }
222 }
223
vesadrm_load_palette_lut(struct vesadrm_device * vesa,const struct drm_format_info * format,struct drm_color_lut * lut)224 static void vesadrm_load_palette_lut(struct vesadrm_device *vesa,
225 const struct drm_format_info *format,
226 struct drm_color_lut *lut)
227 {
228 struct drm_device *dev = &vesa->sysfb.dev;
229 struct drm_crtc *crtc = &vesa->crtc;
230
231 switch (format->format) {
232 case DRM_FORMAT_C8:
233 drm_crtc_load_palette_8(crtc, lut, vesadrm_set_color_lut);
234 break;
235 default:
236 drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n",
237 &format->format);
238 break;
239 }
240 }
241
242 /*
243 * Modesetting
244 */
245
246 static const u64 vesadrm_primary_plane_format_modifiers[] = {
247 DRM_SYSFB_PLANE_FORMAT_MODIFIERS,
248 };
249
vesadrm_primary_plane_helper_atomic_check(struct drm_plane * plane,struct drm_atomic_commit * new_state)250 static int vesadrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
251 struct drm_atomic_commit *new_state)
252 {
253 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev);
254 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane);
255 struct drm_framebuffer *new_fb = new_plane_state->fb;
256 struct drm_crtc_state *new_crtc_state;
257 struct drm_sysfb_crtc_state *new_sysfb_crtc_state;
258 int ret;
259
260 ret = drm_sysfb_plane_helper_atomic_check(plane, new_state);
261 if (ret)
262 return ret;
263 else if (!new_plane_state->visible)
264 return 0;
265
266 /*
267 * Fix up format conversion for specific cases
268 */
269
270 switch (sysfb->fb_format->format) {
271 case DRM_FORMAT_C8:
272 new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
273 new_sysfb_crtc_state = to_drm_sysfb_crtc_state(new_crtc_state);
274
275 switch (new_fb->format->format) {
276 case DRM_FORMAT_XRGB8888:
277 /*
278 * Reduce XRGB8888 to RGB332. Each resulting pixel is an index
279 * into the C8 hardware palette, which stores RGB332 colors.
280 */
281 if (new_sysfb_crtc_state->format->format != DRM_FORMAT_RGB332) {
282 new_sysfb_crtc_state->format =
283 drm_format_info(DRM_FORMAT_RGB332);
284 new_crtc_state->color_mgmt_changed = true;
285 }
286 break;
287 case DRM_FORMAT_C8:
288 /*
289 * Restore original output. Emulation of XRGB8888 set RBG332
290 * output format and hardware palette. This needs to be undone
291 * when we switch back to DRM_FORMAT_C8.
292 */
293 if (new_sysfb_crtc_state->format->format == DRM_FORMAT_RGB332) {
294 new_sysfb_crtc_state->format = sysfb->fb_format;
295 new_crtc_state->color_mgmt_changed = true;
296 }
297 break;
298 }
299 break;
300 }
301
302 return 0;
303 }
304
305 static const struct drm_plane_helper_funcs vesadrm_primary_plane_helper_funcs = {
306 .begin_fb_access = drm_sysfb_plane_helper_begin_fb_access,
307 .end_fb_access = drm_gem_end_shadow_fb_access,
308 .atomic_check = vesadrm_primary_plane_helper_atomic_check,
309 .atomic_update = drm_sysfb_plane_helper_atomic_update,
310 .atomic_disable = drm_sysfb_plane_helper_atomic_disable,
311 .get_scanout_buffer = drm_sysfb_plane_helper_get_scanout_buffer,
312 };
313
314 static const struct drm_plane_funcs vesadrm_primary_plane_funcs = {
315 DRM_SYSFB_PLANE_FUNCS,
316 .destroy = drm_plane_cleanup,
317 };
318
vesadrm_crtc_helper_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_commit * state)319 static void vesadrm_crtc_helper_atomic_flush(struct drm_crtc *crtc,
320 struct drm_atomic_commit *state)
321 {
322 struct drm_device *dev = crtc->dev;
323 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
324 struct vesadrm_device *vesa = to_vesadrm_device(dev);
325 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
326 struct drm_sysfb_crtc_state *sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state);
327
328 /*
329 * The gamma LUT has to be reloaded after changing the primary
330 * plane's color format.
331 */
332 if (crtc_state->enable && crtc_state->color_mgmt_changed) {
333 switch (sysfb->fb_format->format) {
334 /*
335 * Index formats
336 */
337 case DRM_FORMAT_C8:
338 if (sysfb_crtc_state->format->format == DRM_FORMAT_RGB332) {
339 vesadrm_fill_palette_lut(vesa, sysfb_crtc_state->format);
340 } else if (crtc->state->gamma_lut) {
341 vesadrm_load_palette_lut(vesa,
342 sysfb_crtc_state->format,
343 crtc_state->gamma_lut->data);
344 } else {
345 vesadrm_fill_palette_lut(vesa, sysfb_crtc_state->format);
346 }
347 break;
348 /*
349 * Component formats
350 */
351 default:
352 if (sysfb_crtc_state->format == sysfb->fb_format) {
353 if (crtc_state->gamma_lut)
354 vesadrm_load_gamma_lut(vesa,
355 sysfb_crtc_state->format,
356 crtc_state->gamma_lut->data);
357 else
358 vesadrm_fill_gamma_lut(vesa, sysfb_crtc_state->format);
359 } else {
360 vesadrm_fill_gamma_lut(vesa, sysfb_crtc_state->format);
361 }
362 break;
363 }
364 }
365 }
366
367 static const struct drm_crtc_helper_funcs vesadrm_crtc_helper_funcs = {
368 DRM_SYSFB_CRTC_HELPER_FUNCS,
369 .atomic_flush = vesadrm_crtc_helper_atomic_flush,
370 };
371
372 static const struct drm_crtc_funcs vesadrm_crtc_funcs = {
373 DRM_SYSFB_CRTC_FUNCS,
374 .destroy = drm_crtc_cleanup,
375 };
376
377 static const struct drm_encoder_funcs vesadrm_encoder_funcs = {
378 .destroy = drm_encoder_cleanup,
379 };
380
381 static const struct drm_connector_helper_funcs vesadrm_connector_helper_funcs = {
382 DRM_SYSFB_CONNECTOR_HELPER_FUNCS,
383 };
384
385 static const struct drm_connector_funcs vesadrm_connector_funcs = {
386 DRM_SYSFB_CONNECTOR_FUNCS,
387 .destroy = drm_connector_cleanup,
388 };
389
390 static const struct drm_mode_config_funcs vesadrm_mode_config_funcs = {
391 DRM_SYSFB_MODE_CONFIG_FUNCS,
392 };
393
394 /*
395 * Init / Cleanup
396 */
397
vesadrm_device_create(struct drm_driver * drv,struct platform_device * pdev)398 static struct vesadrm_device *vesadrm_device_create(struct drm_driver *drv,
399 struct platform_device *pdev)
400 {
401 const struct sysfb_display_info *dpy;
402 const struct screen_info *si;
403 const struct drm_format_info *format;
404 int width, height, stride;
405 s64 vsize;
406 struct resource resbuf;
407 struct resource *res;
408 struct vesadrm_device *vesa;
409 struct drm_sysfb_device *sysfb;
410 struct drm_device *dev;
411 struct resource *mem = NULL;
412 void __iomem *screen_base;
413 struct drm_plane *primary_plane;
414 struct drm_crtc *crtc;
415 struct drm_encoder *encoder;
416 struct drm_connector *connector;
417 unsigned long max_width, max_height;
418 size_t nformats;
419 int ret;
420
421 dpy = dev_get_platdata(&pdev->dev);
422 if (!dpy)
423 return ERR_PTR(-ENODEV);
424 si = &dpy->screen;
425
426 if (screen_info_video_type(si) != VIDEO_TYPE_VLFB)
427 return ERR_PTR(-ENODEV);
428
429 /*
430 * VESA DRM driver
431 */
432
433 vesa = devm_drm_dev_alloc(&pdev->dev, drv, struct vesadrm_device, sysfb.dev);
434 if (IS_ERR(vesa))
435 return ERR_CAST(vesa);
436 sysfb = &vesa->sysfb;
437 dev = &sysfb->dev;
438 platform_set_drvdata(pdev, dev);
439
440 /*
441 * Hardware settings
442 */
443
444 format = vesadrm_get_format_si(dev, si);
445 if (!format)
446 return ERR_PTR(-EINVAL);
447 width = drm_sysfb_get_width_si(dev, si);
448 if (width < 0)
449 return ERR_PTR(width);
450 height = drm_sysfb_get_height_si(dev, si);
451 if (height < 0)
452 return ERR_PTR(height);
453 res = drm_sysfb_get_memory_si(dev, si, &resbuf);
454 if (!res)
455 return ERR_PTR(-EINVAL);
456 stride = drm_sysfb_get_stride_si(dev, si, format, width, height, resource_size(res));
457 if (stride < 0)
458 return ERR_PTR(stride);
459 vsize = drm_sysfb_get_visible_size_si(dev, si, height, stride, resource_size(res));
460 if (vsize < 0)
461 return ERR_PTR(vsize);
462
463 drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d bytes\n",
464 &format->format, width, height, stride);
465
466 if (!__screen_info_vbe_mode_nonvga(si)) {
467 vesa->cmap_write = vesadrm_vga_cmap_write;
468 } else {
469 #if defined(CONFIG_X86_32)
470 phys_addr_t pmi_base = __screen_info_vesapm_info_base(si);
471
472 if (pmi_base) {
473 const u16 *pmi_addr = phys_to_virt(pmi_base);
474
475 vesa->pmi.PrimaryPalette = (u8 *)pmi_addr + pmi_addr[2];
476 vesa->cmap_write = vesadrm_pmi_cmap_write;
477 } else
478 #endif
479 if (format->is_color_indexed)
480 drm_warn(dev, "hardware palette is unchangeable, colors may be incorrect\n");
481 }
482
483 #if defined(CONFIG_FIRMWARE_EDID)
484 if (drm_edid_header_is_valid(dpy->edid.dummy) == 8)
485 sysfb->edid = dpy->edid.dummy;
486 #endif
487 sysfb->fb_mode = drm_sysfb_mode(width, height, 0, 0);
488 sysfb->fb_format = format;
489 sysfb->fb_pitch = stride;
490 if (vesa->cmap_write)
491 sysfb->fb_gamma_lut_size = VESADRM_GAMMA_LUT_SIZE;
492
493 /*
494 * Memory management
495 */
496
497 ret = devm_aperture_acquire_for_platform_device(pdev, res->start, vsize);
498 if (ret) {
499 drm_err(dev, "could not acquire memory range %pr: %d\n", res, ret);
500 return ERR_PTR(ret);
501 }
502
503 drm_dbg(dev, "using I/O memory framebuffer at %pr\n", res);
504
505 mem = devm_request_mem_region(&pdev->dev, res->start, vsize, drv->name);
506 if (!mem) {
507 /*
508 * We cannot make this fatal. Sometimes this comes from magic
509 * spaces our resource handlers simply don't know about. Use
510 * the I/O-memory resource as-is and try to map that instead.
511 */
512 drm_warn(dev, "could not acquire memory region %pr\n", res);
513 mem = res;
514 }
515
516 screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem));
517 if (!screen_base)
518 return ERR_PTR(-ENOMEM);
519 iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base);
520
521 /*
522 * Modesetting
523 */
524
525 ret = drmm_mode_config_init(dev);
526 if (ret)
527 return ERR_PTR(ret);
528
529 max_width = max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH);
530 max_height = max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT);
531
532 dev->mode_config.min_width = width;
533 dev->mode_config.max_width = max_width;
534 dev->mode_config.min_height = height;
535 dev->mode_config.max_height = max_height;
536 dev->mode_config.preferred_depth = format->depth;
537 dev->mode_config.funcs = &vesadrm_mode_config_funcs;
538
539 /* Primary plane */
540
541 nformats = drm_sysfb_build_fourcc_list(dev, &format->format, 1,
542 vesa->formats, ARRAY_SIZE(vesa->formats));
543
544 primary_plane = &vesa->primary_plane;
545 ret = drm_universal_plane_init(dev, primary_plane, 0, &vesadrm_primary_plane_funcs,
546 vesa->formats, nformats,
547 vesadrm_primary_plane_format_modifiers,
548 DRM_PLANE_TYPE_PRIMARY, NULL);
549 if (ret)
550 return ERR_PTR(ret);
551 drm_plane_helper_add(primary_plane, &vesadrm_primary_plane_helper_funcs);
552 drm_plane_enable_fb_damage_clips(primary_plane);
553
554 /* CRTC */
555
556 crtc = &vesa->crtc;
557 ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
558 &vesadrm_crtc_funcs, NULL);
559 if (ret)
560 return ERR_PTR(ret);
561 drm_crtc_helper_add(crtc, &vesadrm_crtc_helper_funcs);
562
563 if (sysfb->fb_gamma_lut_size) {
564 ret = drm_mode_crtc_set_gamma_size(crtc, sysfb->fb_gamma_lut_size);
565 if (!ret)
566 drm_crtc_enable_color_mgmt(crtc, 0, false, sysfb->fb_gamma_lut_size);
567 }
568
569 /* Encoder */
570
571 encoder = &vesa->encoder;
572 ret = drm_encoder_init(dev, encoder, &vesadrm_encoder_funcs,
573 DRM_MODE_ENCODER_NONE, NULL);
574 if (ret)
575 return ERR_PTR(ret);
576 encoder->possible_crtcs = drm_crtc_mask(crtc);
577
578 /* Connector */
579
580 connector = &vesa->connector;
581 ret = drm_connector_init(dev, connector, &vesadrm_connector_funcs,
582 DRM_MODE_CONNECTOR_Unknown);
583 if (ret)
584 return ERR_PTR(ret);
585 drm_connector_helper_add(connector, &vesadrm_connector_helper_funcs);
586 drm_connector_set_panel_orientation_with_quirk(connector,
587 DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
588 width, height);
589 if (sysfb->edid)
590 drm_connector_attach_edid_property(connector);
591
592 ret = drm_connector_attach_encoder(connector, encoder);
593 if (ret)
594 return ERR_PTR(ret);
595
596 drm_mode_config_reset(dev);
597
598 return vesa;
599 }
600
601 /*
602 * DRM driver
603 */
604
605 DEFINE_DRM_GEM_FOPS(vesadrm_fops);
606
607 static struct drm_driver vesadrm_driver = {
608 DRM_GEM_SHMEM_DRIVER_OPS,
609 DRM_FBDEV_SHMEM_DRIVER_OPS,
610 .name = DRIVER_NAME,
611 .desc = DRIVER_DESC,
612 .major = DRIVER_MAJOR,
613 .minor = DRIVER_MINOR,
614 .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
615 .fops = &vesadrm_fops,
616 };
617
618 /*
619 * Platform driver
620 */
621
vesadrm_pm_suspend(struct device * dev)622 static int vesadrm_pm_suspend(struct device *dev)
623 {
624 struct drm_device *drm = dev_get_drvdata(dev);
625
626 return drm_mode_config_helper_suspend(drm);
627 }
628
vesadrm_pm_resume(struct device * dev)629 static int vesadrm_pm_resume(struct device *dev)
630 {
631 struct drm_device *drm = dev_get_drvdata(dev);
632
633 return drm_mode_config_helper_resume(drm);
634 }
635
636 static DEFINE_SIMPLE_DEV_PM_OPS(vesadrm_pm_ops, vesadrm_pm_suspend, vesadrm_pm_resume);
637
vesadrm_probe(struct platform_device * pdev)638 static int vesadrm_probe(struct platform_device *pdev)
639 {
640 struct vesadrm_device *vesa;
641 struct drm_sysfb_device *sysfb;
642 struct drm_device *dev;
643 int ret;
644
645 vesa = vesadrm_device_create(&vesadrm_driver, pdev);
646 if (IS_ERR(vesa))
647 return PTR_ERR(vesa);
648 sysfb = &vesa->sysfb;
649 dev = &sysfb->dev;
650
651 ret = drm_dev_register(dev, 0);
652 if (ret)
653 return ret;
654
655 drm_client_setup(dev, sysfb->fb_format);
656
657 return 0;
658 }
659
vesadrm_remove(struct platform_device * pdev)660 static void vesadrm_remove(struct platform_device *pdev)
661 {
662 struct drm_device *dev = platform_get_drvdata(pdev);
663
664 drm_dev_unplug(dev);
665 }
666
667 static struct platform_driver vesadrm_platform_driver = {
668 .driver = {
669 .name = "vesa-framebuffer",
670 .pm = pm_sleep_ptr(&vesadrm_pm_ops),
671 },
672 .probe = vesadrm_probe,
673 .remove = vesadrm_remove,
674 };
675
676 module_platform_driver(vesadrm_platform_driver);
677
678 MODULE_DESCRIPTION(DRIVER_DESC);
679 MODULE_LICENSE("GPL");
680