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