riscv:linux:drm
[platform/kernel/linux-starfive.git] / drivers / gpu / drm / verisilicon / vs_crtc.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
4  */
5
6 #include <linux/clk.h>
7 #include <linux/debugfs.h>
8 #include <drm/drm_gem_atomic_helper.h>
9 #include <drm/drm_atomic_helper.h>
10 #include <drm/drm_atomic.h>
11
12 #include <drm/drm_crtc.h>
13 #include <drm/vs_drm.h>
14
15 #include "vs_crtc.h"
16
17 #if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE
18 #include <drm/drm_vblank.h>
19 #endif
20
21 void vs_crtc_destroy(struct drm_crtc *crtc)
22 {
23         struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
24
25         drm_crtc_cleanup(crtc);
26         kfree(vs_crtc);
27 }
28
29 static void vs_crtc_reset(struct drm_crtc *crtc)
30 {
31         struct vs_crtc_state *state;
32
33         if (crtc->state) {
34                 __drm_atomic_helper_crtc_destroy_state(crtc->state);
35
36                 state = to_vs_crtc_state(crtc->state);
37                 kfree(state);
38                 crtc->state = NULL;
39         }
40
41         state = kzalloc(sizeof(*state), GFP_KERNEL);
42         if (state == NULL)
43                 return;
44
45         __drm_atomic_helper_crtc_reset(crtc, &state->base);
46
47         state->sync_mode = VS_SINGLE_DC;
48         state->output_fmt = MEDIA_BUS_FMT_RBG888_1X24;
49         state->encoder_type = DRM_MODE_ENCODER_NONE;
50 #ifdef CONFIG_VERISILICON_MMU
51         state->mmu_prefetch = VS_MMU_PREFETCH_DISABLE;
52 #endif
53 }
54
55 static struct drm_crtc_state *
56 vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
57 {
58         struct vs_crtc_state *ori_state;
59         struct vs_crtc_state *state;
60
61         if (WARN_ON(!crtc->state))
62                 return NULL;
63
64         ori_state = to_vs_crtc_state(crtc->state);
65         state = kzalloc(sizeof(*state), GFP_KERNEL);
66         if (!state)
67                 return NULL;
68
69         __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
70
71         state->sync_mode = ori_state->sync_mode;
72         state->output_fmt = ori_state->output_fmt;
73         state->encoder_type = ori_state->encoder_type;
74         state->bg_color = ori_state->bg_color;
75         state->bpp = ori_state->bpp;
76         state->sync_enable = ori_state->sync_enable;
77         state->dither_enable = ori_state->dither_enable;
78         state->underflow = ori_state->underflow;
79 #ifdef CONFIG_VERISILICON_MMU
80         state->mmu_prefetch = ori_state->mmu_prefetch;
81 #endif
82
83         return &state->base;
84 }
85
86 static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
87                                          struct drm_crtc_state *state)
88 {
89         __drm_atomic_helper_crtc_destroy_state(state);
90         kfree(to_vs_crtc_state(state));
91 }
92
93 static int vs_crtc_atomic_set_property(struct drm_crtc *crtc,
94                                            struct drm_crtc_state *state,
95                                            struct drm_property *property,
96                                            uint64_t val)
97 {
98         struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
99         struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(state);
100
101         if (property == vs_crtc->sync_mode)
102                 vs_crtc_state->sync_mode = val;
103         else if (property == vs_crtc->mmu_prefetch)
104                 vs_crtc_state->mmu_prefetch = val;
105         else if (property == vs_crtc->bg_color)
106                 vs_crtc_state->bg_color = val;
107         else if (property == vs_crtc->panel_sync)
108                 vs_crtc_state->sync_enable = val;
109         else if (property == vs_crtc->dither)
110                 vs_crtc_state->dither_enable = val;
111         else
112                 return -EINVAL;
113
114         return 0;
115 }
116
117 static int vs_crtc_atomic_get_property(struct drm_crtc *crtc,
118                                            const struct drm_crtc_state *state,
119                                            struct drm_property *property,
120                                            uint64_t *val)
121 {
122         struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
123         const struct vs_crtc_state *vs_crtc_state =
124                 container_of(state, const struct vs_crtc_state, base);
125
126         if (property == vs_crtc->sync_mode)
127                 *val = vs_crtc_state->sync_mode;
128         else if (property == vs_crtc->mmu_prefetch)
129                 *val = vs_crtc_state->mmu_prefetch;
130         else if (property == vs_crtc->bg_color)
131                 *val = vs_crtc_state->bg_color;
132         else if (property == vs_crtc->panel_sync)
133                 *val = vs_crtc_state->sync_enable;
134         else if (property == vs_crtc->dither)
135                 *val = vs_crtc_state->dither_enable;
136         else
137                 return -EINVAL;
138
139         return 0;
140 }
141
142 #ifdef CONFIG_DEBUG_FS
143 static int vs_crtc_debugfs_show(struct seq_file *s, void *data)
144 {
145         struct drm_crtc *crtc = s->private;
146         struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
147         struct drm_display_mode *mode = &crtc->state->adjusted_mode;
148
149         seq_printf(s, "crtc[%u]: %s\n", crtc->base.id, crtc->name);
150         seq_printf(s, "\tactive = %d\n", crtc->state->active);
151         seq_printf(s, "\tsize = %dx%d\n", mode->hdisplay, mode->vdisplay);
152         seq_printf(s, "\tbpp = %u\n", crtc_state->bpp);
153         seq_printf(s, "\tunderflow = %d\n", crtc_state->underflow);
154
155         return 0;
156 }
157
158 static int vs_crtc_debugfs_open(struct inode *inode, struct file *file)
159 {
160         return single_open(file, vs_crtc_debugfs_show, inode->i_private);
161 }
162
163 static const struct file_operations vs_crtc_debugfs_fops = {
164         .open           = vs_crtc_debugfs_open,
165         .read           = seq_read,
166         .llseek         = seq_lseek,
167         .release        = single_release,
168 };
169
170 static int vs_crtc_debugfs_init(struct drm_crtc *crtc)
171 {
172         debugfs_create_file("status", 0444, crtc->debugfs_entry,
173                                 crtc, &vs_crtc_debugfs_fops);
174
175         return 0;
176 }
177 #else
178 static int vs_crtc_debugfs_init(struct drm_crtc *crtc)
179 {
180         return 0;
181 }
182 #endif /* CONFIG_DEBUG_FS */
183
184 static int vs_crtc_late_register(struct drm_crtc *crtc)
185 {
186         return vs_crtc_debugfs_init(crtc);
187 }
188
189 static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
190 {
191         struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
192         struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
193
194         vs_crtc->funcs->enable_vblank(vs_crtc->dev, true);
195
196         return 0;
197 }
198
199 static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
200 {
201         struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
202
203         vs_crtc->funcs->enable_vblank(vs_crtc->dev, false);
204 }
205
206 static const struct drm_crtc_funcs vs_crtc_funcs = {
207         .set_config             = drm_atomic_helper_set_config,
208         .destroy                = vs_crtc_destroy,
209         .page_flip              = drm_atomic_helper_page_flip,
210         .reset                  = vs_crtc_reset,
211         .atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
212         .atomic_destroy_state   = vs_crtc_atomic_destroy_state,
213         .atomic_set_property    = vs_crtc_atomic_set_property,
214         .atomic_get_property    = vs_crtc_atomic_get_property,
215         //.gamma_set      = drm_atomic_helper_legacy_gamma_set,
216         .late_register          = vs_crtc_late_register,
217         .enable_vblank          = vs_crtc_enable_vblank,
218         .disable_vblank         = vs_crtc_disable_vblank,
219 };
220
221 static u8 cal_pixel_bits(u32 bus_format)
222 {
223         u8 bpp;
224
225         switch (bus_format) {
226         case MEDIA_BUS_FMT_RGB565_1X16:
227         case MEDIA_BUS_FMT_UYVY8_1X16:
228                 bpp = 16;
229                 break;
230         case MEDIA_BUS_FMT_RGB666_1X18:
231         case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
232                 bpp = 18;
233                 break;
234         case MEDIA_BUS_FMT_UYVY10_1X20:
235                 bpp = 20;
236                 break;
237         case MEDIA_BUS_FMT_BGR888_1X24:
238         case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
239         case MEDIA_BUS_FMT_YUV8_1X24:
240                 bpp = 24;
241                 break;
242         case MEDIA_BUS_FMT_RGB101010_1X30:
243         case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
244         case MEDIA_BUS_FMT_YUV10_1X30:
245                 bpp = 30;
246                 break;
247         default:
248                 bpp = 24;
249                 break;
250         }
251
252         return bpp;
253 }
254
255 static bool vs_crtc_mode_fixup(struct drm_crtc *crtc,
256                                    const struct drm_display_mode *mode,
257                                    struct drm_display_mode *adjusted_mode)
258 {
259         struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
260
261         return vs_crtc->funcs->mode_fixup(vs_crtc->dev, mode, adjusted_mode);
262 }
263
264 static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
265                                         struct drm_atomic_state *state)
266 {
267         struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
268         struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
269
270         vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
271
272         vs_crtc->funcs->enable(vs_crtc->dev, crtc);
273         drm_crtc_vblank_on(crtc);
274
275 }
276
277 static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
278                                         struct drm_atomic_state *state)
279 {
280         struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
281
282         drm_crtc_vblank_off(crtc);
283
284         vs_crtc->funcs->disable(vs_crtc->dev, crtc);
285
286         if (crtc->state->event && !crtc->state->active) {
287                 spin_lock_irq(&crtc->dev->event_lock);
288                 drm_crtc_send_vblank_event(crtc, crtc->state->event);
289                 spin_unlock_irq(&crtc->dev->event_lock);
290
291                 crtc->state->event = NULL;
292         }
293 }
294
295 static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
296                                   struct drm_atomic_state *state)
297 {
298         struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
299                                                                           crtc);
300         //struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,crtc);
301
302         struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
303         struct device *dev = vs_crtc->dev;
304         struct drm_property_blob *blob = crtc->state->gamma_lut;
305         struct drm_color_lut *lut;
306         struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
307
308         if (crtc_state->color_mgmt_changed) {
309                 if ((blob) && (blob->length)) {
310                         lut = blob->data;
311                         vs_crtc->funcs->set_gamma(dev, crtc, lut,
312                                                   blob->length / sizeof(*lut));
313                         vs_crtc->funcs->enable_gamma(dev, crtc, true);
314                 } else {
315                         vs_crtc->funcs->enable_gamma(dev, crtc, false);
316                 }
317         }
318 }
319
320 static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
321                                   struct drm_atomic_state *state)
322 {
323         struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
324         struct drm_pending_vblank_event *event = crtc->state->event;
325
326         vs_crtc->funcs->commit(vs_crtc->dev);
327
328         if (event) {
329                 WARN_ON(drm_crtc_vblank_get(crtc) != 0);
330
331                 spin_lock_irq(&crtc->dev->event_lock);
332                 drm_crtc_arm_vblank_event(crtc, event);
333                 spin_unlock_irq(&crtc->dev->event_lock);
334                 crtc->state->event = NULL;
335         }
336 }
337
338 static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
339         .mode_fixup = vs_crtc_mode_fixup,
340         .atomic_enable  = vs_crtc_atomic_enable,
341         .atomic_disable = vs_crtc_atomic_disable,
342         .atomic_begin   = vs_crtc_atomic_begin,
343         .atomic_flush   = vs_crtc_atomic_flush,
344 };
345
346 static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
347         { VS_SINGLE_DC,                         "single dc mode" },
348         { VS_MULTI_DC_PRIMARY,          "primary dc for multi dc mode" },
349         { VS_MULTI_DC_SECONDARY,        "secondary dc for multi dc mode" },
350 };
351
352 #ifdef CONFIG_VERISILICON_MMU
353 static const struct drm_prop_enum_list vs_mmu_prefetch_enum_list[] = {
354         { VS_MMU_PREFETCH_DISABLE,      "disable mmu prefetch" },
355         { VS_MMU_PREFETCH_ENABLE,       "enable mmu prefetch" },
356 };
357 #endif
358
359 struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
360                                    struct vs_dc_info *info)
361 {
362         struct vs_crtc *crtc;
363         int ret;
364
365         if (!info)
366                 return NULL;
367
368         crtc = kzalloc(sizeof(struct vs_crtc), GFP_KERNEL);
369         if (!crtc)
370                 return NULL;
371
372         ret = drm_crtc_init_with_planes(drm_dev, &crtc->base,
373                                         NULL, NULL, &vs_crtc_funcs,
374                                         info->name ? info->name : NULL);
375         if (ret)
376                 goto err_free_crtc;
377
378         drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
379
380         /* Set up the crtc properties */
381         if (info->pipe_sync) {
382                 crtc->sync_mode = drm_property_create_enum(drm_dev, 0,
383                                         "SYNC_MODE",
384                                         vs_sync_mode_enum_list,
385                                         ARRAY_SIZE(vs_sync_mode_enum_list));
386
387                 if (!crtc->sync_mode)
388                         goto err_cleanup_crts;
389
390                 drm_object_attach_property(&crtc->base.base,
391                                            crtc->sync_mode,
392                                            VS_SINGLE_DC);
393         }
394
395         if (info->gamma_size) {
396                 ret = drm_mode_crtc_set_gamma_size(&crtc->base,
397                                                    info->gamma_size);
398                 if (ret)
399                         goto err_cleanup_crts;
400
401                 drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
402                                            info->gamma_size);
403         }
404
405         if (info->background) {
406                 crtc->bg_color = drm_property_create_range(drm_dev, 0,
407                                                  "BG_COLOR", 0, 0xffffffff);
408
409                 if (!crtc->bg_color)
410                         goto err_cleanup_crts;
411
412                 drm_object_attach_property(&crtc->base.base, crtc->bg_color, 0);
413         }
414
415         if (info->panel_sync) {
416                 crtc->panel_sync = drm_property_create_bool(drm_dev, 0, "SYNC_ENABLED");
417
418                 if (!crtc->panel_sync)
419                         goto err_cleanup_crts;
420
421                 drm_object_attach_property(&crtc->base.base, crtc->panel_sync, 0);
422         }
423
424         crtc->dither = drm_property_create_bool(drm_dev, 0, "DITHER_ENABLED");
425         if (!crtc->dither)
426                 goto err_cleanup_crts;
427
428         drm_object_attach_property(&crtc->base.base, crtc->dither, 0);
429
430 #ifdef CONFIG_VERISILICON_MMU
431         if (info->mmu_prefetch) {
432                 crtc->mmu_prefetch = drm_property_create_enum(drm_dev, 0,
433                                                                 "MMU_PREFETCH",
434                                                                 vs_mmu_prefetch_enum_list,
435                                                                 ARRAY_SIZE(vs_mmu_prefetch_enum_list));
436                 if (!crtc->mmu_prefetch)
437                         goto err_cleanup_crts;
438
439                 drm_object_attach_property(&crtc->base.base,
440                                                                    crtc->mmu_prefetch,
441                                                                    VS_MMU_PREFETCH_DISABLE);
442         }
443 #endif
444
445         crtc->max_bpc = info->max_bpc;
446         crtc->color_formats = info->color_formats;
447         return crtc;
448
449 err_cleanup_crts:
450         drm_crtc_cleanup(&crtc->base);
451
452 err_free_crtc:
453         kfree(crtc);
454         return NULL;
455 }
456
457 void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
458 {
459         struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
460
461         drm_crtc_handle_vblank(crtc);
462
463         vs_crtc_state->underflow = underflow;
464 }