Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
52653 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (C) 2014 Traphandler
4
* Copyright (C) 2014 Free Electrons
5
* Copyright (C) 2014 Atmel
6
*
7
* Author: Jean-Jacques Hiblot <[email protected]>
8
* Author: Boris BREZILLON <[email protected]>
9
*/
10
11
#include <linux/clk.h>
12
#include <linux/irq.h>
13
#include <linux/irqchip.h>
14
#include <linux/mfd/atmel-hlcdc.h>
15
#include <linux/module.h>
16
#include <linux/pm_runtime.h>
17
#include <linux/platform_device.h>
18
19
#include <drm/clients/drm_client_setup.h>
20
#include <drm/drm_atomic.h>
21
#include <drm/drm_atomic_helper.h>
22
#include <drm/drm_drv.h>
23
#include <drm/drm_fbdev_dma.h>
24
#include <drm/drm_fourcc.h>
25
#include <drm/drm_gem_dma_helper.h>
26
#include <drm/drm_gem_framebuffer_helper.h>
27
#include <drm/drm_module.h>
28
#include <drm/drm_print.h>
29
#include <drm/drm_probe_helper.h>
30
#include <drm/drm_vblank.h>
31
32
#include "atmel_hlcdc_dc.h"
33
34
#define ATMEL_HLCDC_LAYER_IRQS_OFFSET 8
35
36
static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9n12_layers[] = {
37
{
38
.name = "base",
39
.formats = &atmel_hlcdc_plane_rgb_formats,
40
.regs_offset = 0x40,
41
.id = 0,
42
.type = ATMEL_HLCDC_BASE_LAYER,
43
.cfgs_offset = 0x2c,
44
.layout = {
45
.xstride = { 2 },
46
.default_color = 3,
47
.general_config = 4,
48
},
49
.clut_offset = 0x400,
50
},
51
};
52
53
static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9n12 = {
54
.min_width = 0,
55
.min_height = 0,
56
.max_width = 1280,
57
.max_height = 860,
58
.max_spw = 0x3f,
59
.max_vpw = 0x3f,
60
.max_hpw = 0xff,
61
.conflicting_output_formats = true,
62
.nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9n12_layers),
63
.layers = atmel_hlcdc_at91sam9n12_layers,
64
.ops = &atmel_hlcdc_ops,
65
};
66
67
static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = {
68
{
69
.name = "base",
70
.formats = &atmel_hlcdc_plane_rgb_formats,
71
.regs_offset = 0x40,
72
.id = 0,
73
.type = ATMEL_HLCDC_BASE_LAYER,
74
.cfgs_offset = 0x2c,
75
.layout = {
76
.xstride = { 2 },
77
.default_color = 3,
78
.general_config = 4,
79
.disc_pos = 5,
80
.disc_size = 6,
81
},
82
.clut_offset = 0x400,
83
},
84
{
85
.name = "overlay1",
86
.formats = &atmel_hlcdc_plane_rgb_formats,
87
.regs_offset = 0x100,
88
.id = 1,
89
.type = ATMEL_HLCDC_OVERLAY_LAYER,
90
.cfgs_offset = 0x2c,
91
.layout = {
92
.pos = 2,
93
.size = 3,
94
.xstride = { 4 },
95
.pstride = { 5 },
96
.default_color = 6,
97
.chroma_key = 7,
98
.chroma_key_mask = 8,
99
.general_config = 9,
100
},
101
.clut_offset = 0x800,
102
},
103
{
104
.name = "high-end-overlay",
105
.formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
106
.regs_offset = 0x280,
107
.id = 2,
108
.type = ATMEL_HLCDC_OVERLAY_LAYER,
109
.cfgs_offset = 0x4c,
110
.layout = {
111
.pos = 2,
112
.size = 3,
113
.memsize = 4,
114
.xstride = { 5, 7 },
115
.pstride = { 6, 8 },
116
.default_color = 9,
117
.chroma_key = 10,
118
.chroma_key_mask = 11,
119
.general_config = 12,
120
.scaler_config = 13,
121
.csc = 14,
122
},
123
.clut_offset = 0x1000,
124
},
125
{
126
.name = "cursor",
127
.formats = &atmel_hlcdc_plane_rgb_formats,
128
.regs_offset = 0x340,
129
.id = 3,
130
.type = ATMEL_HLCDC_CURSOR_LAYER,
131
.max_width = 128,
132
.max_height = 128,
133
.cfgs_offset = 0x2c,
134
.layout = {
135
.pos = 2,
136
.size = 3,
137
.xstride = { 4 },
138
.default_color = 6,
139
.chroma_key = 7,
140
.chroma_key_mask = 8,
141
.general_config = 9,
142
},
143
.clut_offset = 0x1400,
144
},
145
};
146
147
static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9x5 = {
148
.min_width = 0,
149
.min_height = 0,
150
.max_width = 800,
151
.max_height = 600,
152
.max_spw = 0x3f,
153
.max_vpw = 0x3f,
154
.max_hpw = 0xff,
155
.conflicting_output_formats = true,
156
.nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9x5_layers),
157
.layers = atmel_hlcdc_at91sam9x5_layers,
158
.ops = &atmel_hlcdc_ops,
159
};
160
161
static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
162
{
163
.name = "base",
164
.formats = &atmel_hlcdc_plane_rgb_formats,
165
.regs_offset = 0x40,
166
.id = 0,
167
.type = ATMEL_HLCDC_BASE_LAYER,
168
.cfgs_offset = 0x2c,
169
.layout = {
170
.xstride = { 2 },
171
.default_color = 3,
172
.general_config = 4,
173
.disc_pos = 5,
174
.disc_size = 6,
175
},
176
.clut_offset = 0x600,
177
},
178
{
179
.name = "overlay1",
180
.formats = &atmel_hlcdc_plane_rgb_formats,
181
.regs_offset = 0x140,
182
.id = 1,
183
.type = ATMEL_HLCDC_OVERLAY_LAYER,
184
.cfgs_offset = 0x2c,
185
.layout = {
186
.pos = 2,
187
.size = 3,
188
.xstride = { 4 },
189
.pstride = { 5 },
190
.default_color = 6,
191
.chroma_key = 7,
192
.chroma_key_mask = 8,
193
.general_config = 9,
194
},
195
.clut_offset = 0xa00,
196
},
197
{
198
.name = "overlay2",
199
.formats = &atmel_hlcdc_plane_rgb_formats,
200
.regs_offset = 0x240,
201
.id = 2,
202
.type = ATMEL_HLCDC_OVERLAY_LAYER,
203
.cfgs_offset = 0x2c,
204
.layout = {
205
.pos = 2,
206
.size = 3,
207
.xstride = { 4 },
208
.pstride = { 5 },
209
.default_color = 6,
210
.chroma_key = 7,
211
.chroma_key_mask = 8,
212
.general_config = 9,
213
},
214
.clut_offset = 0xe00,
215
},
216
{
217
.name = "high-end-overlay",
218
.formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
219
.regs_offset = 0x340,
220
.id = 3,
221
.type = ATMEL_HLCDC_OVERLAY_LAYER,
222
.cfgs_offset = 0x4c,
223
.layout = {
224
.pos = 2,
225
.size = 3,
226
.memsize = 4,
227
.xstride = { 5, 7 },
228
.pstride = { 6, 8 },
229
.default_color = 9,
230
.chroma_key = 10,
231
.chroma_key_mask = 11,
232
.general_config = 12,
233
.scaler_config = 13,
234
.phicoeffs = {
235
.x = 17,
236
.y = 33,
237
},
238
.csc = 14,
239
},
240
.clut_offset = 0x1200,
241
},
242
{
243
.name = "cursor",
244
.formats = &atmel_hlcdc_plane_rgb_formats,
245
.regs_offset = 0x440,
246
.id = 4,
247
.type = ATMEL_HLCDC_CURSOR_LAYER,
248
.max_width = 128,
249
.max_height = 128,
250
.cfgs_offset = 0x2c,
251
.layout = {
252
.pos = 2,
253
.size = 3,
254
.xstride = { 4 },
255
.pstride = { 5 },
256
.default_color = 6,
257
.chroma_key = 7,
258
.chroma_key_mask = 8,
259
.general_config = 9,
260
.scaler_config = 13,
261
},
262
.clut_offset = 0x1600,
263
},
264
};
265
266
static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = {
267
.min_width = 0,
268
.min_height = 0,
269
.max_width = 2048,
270
.max_height = 2048,
271
.max_spw = 0x3f,
272
.max_vpw = 0x3f,
273
.max_hpw = 0x1ff,
274
.conflicting_output_formats = true,
275
.nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers),
276
.layers = atmel_hlcdc_sama5d3_layers,
277
.ops = &atmel_hlcdc_ops,
278
};
279
280
static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = {
281
{
282
.name = "base",
283
.formats = &atmel_hlcdc_plane_rgb_formats,
284
.regs_offset = 0x40,
285
.id = 0,
286
.type = ATMEL_HLCDC_BASE_LAYER,
287
.cfgs_offset = 0x2c,
288
.layout = {
289
.xstride = { 2 },
290
.default_color = 3,
291
.general_config = 4,
292
.disc_pos = 5,
293
.disc_size = 6,
294
},
295
.clut_offset = 0x600,
296
},
297
{
298
.name = "overlay1",
299
.formats = &atmel_hlcdc_plane_rgb_formats,
300
.regs_offset = 0x140,
301
.id = 1,
302
.type = ATMEL_HLCDC_OVERLAY_LAYER,
303
.cfgs_offset = 0x2c,
304
.layout = {
305
.pos = 2,
306
.size = 3,
307
.xstride = { 4 },
308
.pstride = { 5 },
309
.default_color = 6,
310
.chroma_key = 7,
311
.chroma_key_mask = 8,
312
.general_config = 9,
313
},
314
.clut_offset = 0xa00,
315
},
316
{
317
.name = "overlay2",
318
.formats = &atmel_hlcdc_plane_rgb_formats,
319
.regs_offset = 0x240,
320
.id = 2,
321
.type = ATMEL_HLCDC_OVERLAY_LAYER,
322
.cfgs_offset = 0x2c,
323
.layout = {
324
.pos = 2,
325
.size = 3,
326
.xstride = { 4 },
327
.pstride = { 5 },
328
.default_color = 6,
329
.chroma_key = 7,
330
.chroma_key_mask = 8,
331
.general_config = 9,
332
},
333
.clut_offset = 0xe00,
334
},
335
{
336
.name = "high-end-overlay",
337
.formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
338
.regs_offset = 0x340,
339
.id = 3,
340
.type = ATMEL_HLCDC_OVERLAY_LAYER,
341
.cfgs_offset = 0x4c,
342
.layout = {
343
.pos = 2,
344
.size = 3,
345
.memsize = 4,
346
.xstride = { 5, 7 },
347
.pstride = { 6, 8 },
348
.default_color = 9,
349
.chroma_key = 10,
350
.chroma_key_mask = 11,
351
.general_config = 12,
352
.scaler_config = 13,
353
.phicoeffs = {
354
.x = 17,
355
.y = 33,
356
},
357
.csc = 14,
358
},
359
.clut_offset = 0x1200,
360
},
361
};
362
363
static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d4 = {
364
.min_width = 0,
365
.min_height = 0,
366
.max_width = 2048,
367
.max_height = 2048,
368
.max_spw = 0xff,
369
.max_vpw = 0xff,
370
.max_hpw = 0x3ff,
371
.nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d4_layers),
372
.layers = atmel_hlcdc_sama5d4_layers,
373
.ops = &atmel_hlcdc_ops,
374
};
375
376
static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sam9x60_layers[] = {
377
{
378
.name = "base",
379
.formats = &atmel_hlcdc_plane_rgb_formats,
380
.regs_offset = 0x60,
381
.id = 0,
382
.type = ATMEL_HLCDC_BASE_LAYER,
383
.cfgs_offset = 0x2c,
384
.layout = {
385
.xstride = { 2 },
386
.default_color = 3,
387
.general_config = 4,
388
.disc_pos = 5,
389
.disc_size = 6,
390
},
391
.clut_offset = 0x600,
392
},
393
{
394
.name = "overlay1",
395
.formats = &atmel_hlcdc_plane_rgb_formats,
396
.regs_offset = 0x160,
397
.id = 1,
398
.type = ATMEL_HLCDC_OVERLAY_LAYER,
399
.cfgs_offset = 0x2c,
400
.layout = {
401
.pos = 2,
402
.size = 3,
403
.xstride = { 4 },
404
.pstride = { 5 },
405
.default_color = 6,
406
.chroma_key = 7,
407
.chroma_key_mask = 8,
408
.general_config = 9,
409
},
410
.clut_offset = 0xa00,
411
},
412
{
413
.name = "overlay2",
414
.formats = &atmel_hlcdc_plane_rgb_formats,
415
.regs_offset = 0x260,
416
.id = 2,
417
.type = ATMEL_HLCDC_OVERLAY_LAYER,
418
.cfgs_offset = 0x2c,
419
.layout = {
420
.pos = 2,
421
.size = 3,
422
.xstride = { 4 },
423
.pstride = { 5 },
424
.default_color = 6,
425
.chroma_key = 7,
426
.chroma_key_mask = 8,
427
.general_config = 9,
428
},
429
.clut_offset = 0xe00,
430
},
431
{
432
.name = "high-end-overlay",
433
.formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
434
.regs_offset = 0x360,
435
.id = 3,
436
.type = ATMEL_HLCDC_OVERLAY_LAYER,
437
.cfgs_offset = 0x4c,
438
.layout = {
439
.pos = 2,
440
.size = 3,
441
.memsize = 4,
442
.xstride = { 5, 7 },
443
.pstride = { 6, 8 },
444
.default_color = 9,
445
.chroma_key = 10,
446
.chroma_key_mask = 11,
447
.general_config = 12,
448
.scaler_config = 13,
449
.phicoeffs = {
450
.x = 17,
451
.y = 33,
452
},
453
.csc = 14,
454
},
455
.clut_offset = 0x1200,
456
},
457
};
458
459
static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sam9x60 = {
460
.min_width = 0,
461
.min_height = 0,
462
.max_width = 2048,
463
.max_height = 2048,
464
.max_spw = 0xff,
465
.max_vpw = 0xff,
466
.max_hpw = 0x3ff,
467
.fixed_clksrc = true,
468
.nlayers = ARRAY_SIZE(atmel_hlcdc_sam9x60_layers),
469
.layers = atmel_hlcdc_sam9x60_layers,
470
.ops = &atmel_hlcdc_ops,
471
};
472
473
static const struct atmel_hlcdc_layer_desc atmel_xlcdc_sam9x75_layers[] = {
474
{
475
.name = "base",
476
.formats = &atmel_hlcdc_plane_rgb_formats,
477
.regs_offset = 0x60,
478
.id = 0,
479
.type = ATMEL_HLCDC_BASE_LAYER,
480
.cfgs_offset = 0x1c,
481
.layout = {
482
.xstride = { 2 },
483
.default_color = 3,
484
.general_config = 4,
485
.disc_pos = 5,
486
.disc_size = 6,
487
},
488
.clut_offset = 0x700,
489
},
490
{
491
.name = "overlay1",
492
.formats = &atmel_hlcdc_plane_rgb_formats,
493
.regs_offset = 0x160,
494
.id = 1,
495
.type = ATMEL_HLCDC_OVERLAY_LAYER,
496
.cfgs_offset = 0x1c,
497
.layout = {
498
.pos = 2,
499
.size = 3,
500
.xstride = { 4 },
501
.pstride = { 5 },
502
.default_color = 6,
503
.chroma_key = 7,
504
.chroma_key_mask = 8,
505
.general_config = 9,
506
},
507
.clut_offset = 0xb00,
508
},
509
{
510
.name = "overlay2",
511
.formats = &atmel_hlcdc_plane_rgb_formats,
512
.regs_offset = 0x260,
513
.id = 2,
514
.type = ATMEL_HLCDC_OVERLAY_LAYER,
515
.cfgs_offset = 0x1c,
516
.layout = {
517
.pos = 2,
518
.size = 3,
519
.xstride = { 4 },
520
.pstride = { 5 },
521
.default_color = 6,
522
.chroma_key = 7,
523
.chroma_key_mask = 8,
524
.general_config = 9,
525
},
526
.clut_offset = 0xf00,
527
},
528
{
529
.name = "high-end-overlay",
530
.formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
531
.regs_offset = 0x360,
532
.id = 3,
533
.type = ATMEL_HLCDC_OVERLAY_LAYER,
534
.cfgs_offset = 0x30,
535
.layout = {
536
.pos = 2,
537
.size = 3,
538
.memsize = 4,
539
.xstride = { 5, 7 },
540
.pstride = { 6, 8 },
541
.default_color = 9,
542
.chroma_key = 10,
543
.chroma_key_mask = 11,
544
.general_config = 12,
545
.csc = 16,
546
.scaler_config = 23,
547
.vxs_config = 30,
548
.hxs_config = 31,
549
},
550
.clut_offset = 0x1300,
551
},
552
};
553
554
static const struct atmel_hlcdc_dc_desc atmel_xlcdc_dc_sam9x75 = {
555
.min_width = 0,
556
.min_height = 0,
557
.max_width = 2048,
558
.max_height = 2048,
559
.max_spw = 0x3ff,
560
.max_vpw = 0x3ff,
561
.max_hpw = 0x3ff,
562
.fixed_clksrc = true,
563
.is_xlcdc = true,
564
.nlayers = ARRAY_SIZE(atmel_xlcdc_sam9x75_layers),
565
.layers = atmel_xlcdc_sam9x75_layers,
566
.ops = &atmel_xlcdc_ops,
567
};
568
569
static const struct of_device_id atmel_hlcdc_of_match[] = {
570
{
571
.compatible = "atmel,at91sam9n12-hlcdc",
572
.data = &atmel_hlcdc_dc_at91sam9n12,
573
},
574
{
575
.compatible = "atmel,at91sam9x5-hlcdc",
576
.data = &atmel_hlcdc_dc_at91sam9x5,
577
},
578
{
579
.compatible = "atmel,sama5d2-hlcdc",
580
.data = &atmel_hlcdc_dc_sama5d4,
581
},
582
{
583
.compatible = "atmel,sama5d3-hlcdc",
584
.data = &atmel_hlcdc_dc_sama5d3,
585
},
586
{
587
.compatible = "atmel,sama5d4-hlcdc",
588
.data = &atmel_hlcdc_dc_sama5d4,
589
},
590
{
591
.compatible = "microchip,sam9x60-hlcdc",
592
.data = &atmel_hlcdc_dc_sam9x60,
593
},
594
{
595
.compatible = "microchip,sam9x75-xlcdc",
596
.data = &atmel_xlcdc_dc_sam9x75,
597
},
598
{ /* sentinel */ },
599
};
600
MODULE_DEVICE_TABLE(of, atmel_hlcdc_of_match);
601
602
enum drm_mode_status
603
atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
604
const struct drm_display_mode *mode)
605
{
606
int vfront_porch = mode->vsync_start - mode->vdisplay;
607
int vback_porch = mode->vtotal - mode->vsync_end;
608
int vsync_len = mode->vsync_end - mode->vsync_start;
609
int hfront_porch = mode->hsync_start - mode->hdisplay;
610
int hback_porch = mode->htotal - mode->hsync_end;
611
int hsync_len = mode->hsync_end - mode->hsync_start;
612
613
if (hsync_len > dc->desc->max_spw + 1 || hsync_len < 1)
614
return MODE_HSYNC;
615
616
if (vsync_len > dc->desc->max_spw + 1 || vsync_len < 1)
617
return MODE_VSYNC;
618
619
if (hfront_porch > dc->desc->max_hpw + 1 || hfront_porch < 1 ||
620
hback_porch > dc->desc->max_hpw + 1 || hback_porch < 1 ||
621
mode->hdisplay < 1)
622
return MODE_H_ILLEGAL;
623
624
if (vfront_porch > dc->desc->max_vpw + 1 || vfront_porch < 1 ||
625
vback_porch > dc->desc->max_vpw || vback_porch < 0 ||
626
mode->vdisplay < 1)
627
return MODE_V_ILLEGAL;
628
629
return MODE_OK;
630
}
631
632
static void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
633
{
634
if (!layer)
635
return;
636
637
if (layer->desc->type == ATMEL_HLCDC_BASE_LAYER ||
638
layer->desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
639
layer->desc->type == ATMEL_HLCDC_CURSOR_LAYER)
640
atmel_hlcdc_plane_irq(atmel_hlcdc_layer_to_plane(layer));
641
}
642
643
static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
644
{
645
struct drm_device *dev = data;
646
struct atmel_hlcdc_dc *dc = dev->dev_private;
647
unsigned long status;
648
unsigned int imr, isr;
649
int i;
650
651
regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr);
652
regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
653
status = imr & isr;
654
if (!status)
655
return IRQ_NONE;
656
657
if (status & ATMEL_HLCDC_SOF)
658
atmel_hlcdc_crtc_irq(dc->crtc);
659
660
for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
661
if (ATMEL_HLCDC_LAYER_STATUS(i) & status)
662
atmel_hlcdc_layer_irq(dc->layers[i]);
663
}
664
665
return IRQ_HANDLED;
666
}
667
668
static void atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev)
669
{
670
struct atmel_hlcdc_dc *dc = dev->dev_private;
671
unsigned int cfg = 0;
672
int i;
673
674
/* Enable interrupts on activated layers */
675
for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
676
if (dc->layers[i])
677
cfg |= ATMEL_HLCDC_LAYER_STATUS(i);
678
}
679
680
regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg);
681
}
682
683
static void atmel_hlcdc_dc_irq_disable(struct drm_device *dev)
684
{
685
struct atmel_hlcdc_dc *dc = dev->dev_private;
686
unsigned int isr;
687
688
regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff);
689
regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
690
}
691
692
static int atmel_hlcdc_dc_irq_install(struct drm_device *dev, unsigned int irq)
693
{
694
int ret;
695
696
atmel_hlcdc_dc_irq_disable(dev);
697
698
ret = devm_request_irq(dev->dev, irq, atmel_hlcdc_dc_irq_handler, 0,
699
dev->driver->name, dev);
700
if (ret)
701
return ret;
702
703
atmel_hlcdc_dc_irq_postinstall(dev);
704
705
return 0;
706
}
707
708
static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev)
709
{
710
atmel_hlcdc_dc_irq_disable(dev);
711
}
712
713
static const struct drm_mode_config_funcs mode_config_funcs = {
714
.fb_create = drm_gem_fb_create,
715
.atomic_check = drm_atomic_helper_check,
716
.atomic_commit = drm_atomic_helper_commit,
717
};
718
719
static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
720
{
721
struct atmel_hlcdc_dc *dc = dev->dev_private;
722
int ret;
723
724
drm_mode_config_init(dev);
725
726
ret = atmel_hlcdc_create_planes(dev);
727
if (ret) {
728
drm_err(dev, "failed to create planes: %d\n", ret);
729
return ret;
730
}
731
732
ret = atmel_hlcdc_crtc_create(dev);
733
if (ret) {
734
drm_err(dev, "failed to create crtc\n");
735
return ret;
736
}
737
738
ret = atmel_hlcdc_create_outputs(dev);
739
if (ret) {
740
drm_err(dev, "failed to create HLCDC outputs: %d\n", ret);
741
return ret;
742
}
743
744
dev->mode_config.min_width = dc->desc->min_width;
745
dev->mode_config.min_height = dc->desc->min_height;
746
dev->mode_config.max_width = dc->desc->max_width;
747
dev->mode_config.max_height = dc->desc->max_height;
748
dev->mode_config.funcs = &mode_config_funcs;
749
dev->mode_config.async_page_flip = true;
750
751
return 0;
752
}
753
754
static struct atmel_hlcdc_dc *atmel_hlcdc_dc_of_dev(struct drm_device *dev)
755
{
756
return container_of(dev, struct atmel_hlcdc_dc, dev);
757
}
758
759
static int atmel_hlcdc_dc_load(struct drm_device *dev)
760
{
761
struct platform_device *pdev = to_platform_device(dev->dev);
762
const struct of_device_id *match;
763
struct atmel_hlcdc_dc *dc = atmel_hlcdc_dc_of_dev(dev);
764
int ret;
765
766
match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node);
767
if (!match) {
768
dev_err(&pdev->dev, "invalid compatible string\n");
769
return -ENODEV;
770
}
771
772
if (!match->data) {
773
dev_err(&pdev->dev, "invalid hlcdc description\n");
774
return -EINVAL;
775
}
776
777
dc->desc = match->data;
778
dc->hlcdc = dev_get_drvdata(dev->dev->parent);
779
dev->dev_private = dc;
780
781
ret = clk_prepare_enable(dc->hlcdc->periph_clk);
782
if (ret) {
783
drm_err(dev, "failed to enable periph_clk\n");
784
return ret;
785
}
786
787
pm_runtime_enable(dev->dev);
788
789
ret = drm_vblank_init(dev, 1);
790
if (ret < 0) {
791
drm_err(dev, "failed to initialize vblank\n");
792
goto err_periph_clk_disable;
793
}
794
795
ret = atmel_hlcdc_dc_modeset_init(dev);
796
if (ret < 0) {
797
drm_err(dev, "failed to initialize mode setting\n");
798
goto err_periph_clk_disable;
799
}
800
801
drm_mode_config_reset(dev);
802
803
pm_runtime_get_sync(dev->dev);
804
ret = atmel_hlcdc_dc_irq_install(dev, dc->hlcdc->irq);
805
pm_runtime_put_sync(dev->dev);
806
if (ret < 0) {
807
drm_err(dev, "failed to install IRQ handler\n");
808
goto err_periph_clk_disable;
809
}
810
811
platform_set_drvdata(pdev, dev);
812
813
drm_kms_helper_poll_init(dev);
814
815
return 0;
816
817
err_periph_clk_disable:
818
pm_runtime_disable(dev->dev);
819
clk_disable_unprepare(dc->hlcdc->periph_clk);
820
821
return ret;
822
}
823
824
static void atmel_hlcdc_dc_unload(struct drm_device *dev)
825
{
826
struct atmel_hlcdc_dc *dc = dev->dev_private;
827
828
drm_kms_helper_poll_fini(dev);
829
drm_atomic_helper_shutdown(dev);
830
drm_mode_config_cleanup(dev);
831
832
pm_runtime_get_sync(dev->dev);
833
atmel_hlcdc_dc_irq_uninstall(dev);
834
pm_runtime_put_sync(dev->dev);
835
836
dev->dev_private = NULL;
837
838
pm_runtime_disable(dev->dev);
839
clk_disable_unprepare(dc->hlcdc->periph_clk);
840
}
841
842
DEFINE_DRM_GEM_DMA_FOPS(fops);
843
844
static const struct drm_driver atmel_hlcdc_dc_driver = {
845
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
846
DRM_GEM_DMA_DRIVER_OPS,
847
DRM_FBDEV_DMA_DRIVER_OPS,
848
.fops = &fops,
849
.name = "atmel-hlcdc",
850
.desc = "Atmel HLCD Controller DRM",
851
.major = 1,
852
.minor = 0,
853
};
854
855
static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
856
{
857
struct atmel_hlcdc_dc *dc;
858
struct drm_device *ddev;
859
int ret;
860
861
if (drm_firmware_drivers_only())
862
return -ENODEV;
863
864
dc = devm_drm_dev_alloc(&pdev->dev, &atmel_hlcdc_dc_driver, struct atmel_hlcdc_dc, dev);
865
if (IS_ERR(dc))
866
return PTR_ERR(dc);
867
ddev = &dc->dev;
868
869
ret = atmel_hlcdc_dc_load(ddev);
870
if (ret)
871
return ret;
872
873
ret = drm_dev_register(ddev, 0);
874
if (ret)
875
goto err_unload;
876
877
drm_client_setup_with_fourcc(ddev, DRM_FORMAT_RGB888);
878
879
return 0;
880
881
err_unload:
882
atmel_hlcdc_dc_unload(ddev);
883
884
return ret;
885
}
886
887
static void atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
888
{
889
struct drm_device *ddev = platform_get_drvdata(pdev);
890
891
drm_dev_unregister(ddev);
892
atmel_hlcdc_dc_unload(ddev);
893
}
894
895
static void atmel_hlcdc_dc_drm_shutdown(struct platform_device *pdev)
896
{
897
drm_atomic_helper_shutdown(platform_get_drvdata(pdev));
898
}
899
900
static int atmel_hlcdc_dc_drm_suspend(struct device *dev)
901
{
902
struct drm_device *drm_dev = dev_get_drvdata(dev);
903
struct atmel_hlcdc_dc *dc = drm_dev->dev_private;
904
struct regmap *regmap = dc->hlcdc->regmap;
905
struct drm_atomic_state *state;
906
907
state = drm_atomic_helper_suspend(drm_dev);
908
if (IS_ERR(state))
909
return PTR_ERR(state);
910
911
dc->suspend.state = state;
912
913
regmap_read(regmap, ATMEL_HLCDC_IMR, &dc->suspend.imr);
914
regmap_write(regmap, ATMEL_HLCDC_IDR, dc->suspend.imr);
915
clk_disable_unprepare(dc->hlcdc->periph_clk);
916
917
return 0;
918
}
919
920
static int atmel_hlcdc_dc_drm_resume(struct device *dev)
921
{
922
struct drm_device *drm_dev = dev_get_drvdata(dev);
923
struct atmel_hlcdc_dc *dc = drm_dev->dev_private;
924
925
clk_prepare_enable(dc->hlcdc->periph_clk);
926
regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, dc->suspend.imr);
927
928
return drm_atomic_helper_resume(drm_dev, dc->suspend.state);
929
}
930
931
static DEFINE_SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops,
932
atmel_hlcdc_dc_drm_suspend,
933
atmel_hlcdc_dc_drm_resume);
934
935
static const struct of_device_id atmel_hlcdc_dc_of_match[] = {
936
{ .compatible = "atmel,hlcdc-display-controller" },
937
{ },
938
};
939
940
static struct platform_driver atmel_hlcdc_dc_platform_driver = {
941
.probe = atmel_hlcdc_dc_drm_probe,
942
.remove = atmel_hlcdc_dc_drm_remove,
943
.shutdown = atmel_hlcdc_dc_drm_shutdown,
944
.driver = {
945
.name = "atmel-hlcdc-display-controller",
946
.pm = pm_sleep_ptr(&atmel_hlcdc_dc_drm_pm_ops),
947
.of_match_table = atmel_hlcdc_dc_of_match,
948
},
949
};
950
drm_module_platform_driver(atmel_hlcdc_dc_platform_driver);
951
952
MODULE_AUTHOR("Jean-Jacques Hiblot <[email protected]>");
953
MODULE_AUTHOR("Boris Brezillon <[email protected]>");
954
MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver");
955
MODULE_LICENSE("GPL");
956
MODULE_ALIAS("platform:atmel-hlcdc-dc");
957
958