]> Git Repo - linux.git/blob - drivers/gpu/drm/logicvc/logicvc_crtc.c
Merge tag 'linux-watchdog-6.14-rc1' of git://www.linux-watchdog.org/linux-watchdog
[linux.git] / drivers / gpu / drm / logicvc / logicvc_crtc.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2019-2022 Bootlin
4  * Author: Paul Kocialkowski <[email protected]>
5  */
6
7 #include <linux/of.h>
8 #include <linux/of_graph.h>
9 #include <linux/types.h>
10 #include <linux/workqueue.h>
11
12 #include <drm/drm_atomic_helper.h>
13 #include <drm/drm_crtc.h>
14 #include <drm/drm_drv.h>
15 #include <drm/drm_gem_dma_helper.h>
16 #include <drm/drm_print.h>
17 #include <drm/drm_vblank.h>
18
19 #include "logicvc_crtc.h"
20 #include "logicvc_drm.h"
21 #include "logicvc_interface.h"
22 #include "logicvc_layer.h"
23 #include "logicvc_regs.h"
24
25 #define logicvc_crtc(c) \
26         container_of(c, struct logicvc_crtc, drm_crtc)
27
28 static enum drm_mode_status
29 logicvc_crtc_mode_valid(struct drm_crtc *drm_crtc,
30                         const struct drm_display_mode *mode)
31 {
32         if (mode->flags & DRM_MODE_FLAG_INTERLACE)
33                 return -EINVAL;
34
35         return 0;
36 }
37
38 static void logicvc_crtc_atomic_begin(struct drm_crtc *drm_crtc,
39                                       struct drm_atomic_state *state)
40 {
41         struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc);
42         struct drm_crtc_state *old_state =
43                 drm_atomic_get_old_crtc_state(state, drm_crtc);
44         struct drm_device *drm_dev = drm_crtc->dev;
45         unsigned long flags;
46
47         /*
48          * We need to grab the pending event here if vblank was already enabled
49          * since we won't get a call to atomic_enable to grab it.
50          */
51         if (drm_crtc->state->event && old_state->active) {
52                 spin_lock_irqsave(&drm_dev->event_lock, flags);
53                 WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0);
54
55                 crtc->event = drm_crtc->state->event;
56                 drm_crtc->state->event = NULL;
57
58                 spin_unlock_irqrestore(&drm_dev->event_lock, flags);
59         }
60 }
61
62 static void logicvc_crtc_atomic_enable(struct drm_crtc *drm_crtc,
63                                        struct drm_atomic_state *state)
64 {
65         struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc);
66         struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
67         struct drm_crtc_state *old_state =
68                 drm_atomic_get_old_crtc_state(state, drm_crtc);
69         struct drm_crtc_state *new_state =
70                 drm_atomic_get_new_crtc_state(state, drm_crtc);
71         struct drm_display_mode *mode = &new_state->adjusted_mode;
72
73         struct drm_device *drm_dev = drm_crtc->dev;
74         unsigned int hact, hfp, hsl, hbp;
75         unsigned int vact, vfp, vsl, vbp;
76         unsigned long flags;
77         u32 ctrl;
78
79         /* Timings */
80
81         hact = mode->hdisplay;
82         hfp = mode->hsync_start - mode->hdisplay;
83         hsl = mode->hsync_end - mode->hsync_start;
84         hbp = mode->htotal - mode->hsync_end;
85
86         vact = mode->vdisplay;
87         vfp = mode->vsync_start - mode->vdisplay;
88         vsl = mode->vsync_end - mode->vsync_start;
89         vbp = mode->vtotal - mode->vsync_end;
90
91         regmap_write(logicvc->regmap, LOGICVC_HSYNC_FRONT_PORCH_REG, hfp - 1);
92         regmap_write(logicvc->regmap, LOGICVC_HSYNC_REG, hsl - 1);
93         regmap_write(logicvc->regmap, LOGICVC_HSYNC_BACK_PORCH_REG, hbp - 1);
94         regmap_write(logicvc->regmap, LOGICVC_HRES_REG, hact - 1);
95
96         regmap_write(logicvc->regmap, LOGICVC_VSYNC_FRONT_PORCH_REG, vfp - 1);
97         regmap_write(logicvc->regmap, LOGICVC_VSYNC_REG, vsl - 1);
98         regmap_write(logicvc->regmap, LOGICVC_VSYNC_BACK_PORCH_REG, vbp - 1);
99         regmap_write(logicvc->regmap, LOGICVC_VRES_REG, vact - 1);
100
101         /* Signals */
102
103         ctrl = LOGICVC_CTRL_HSYNC_ENABLE | LOGICVC_CTRL_VSYNC_ENABLE |
104                LOGICVC_CTRL_DE_ENABLE;
105
106         if (mode->flags & DRM_MODE_FLAG_NHSYNC)
107                 ctrl |= LOGICVC_CTRL_HSYNC_INVERT;
108
109         if (mode->flags & DRM_MODE_FLAG_NVSYNC)
110                 ctrl |= LOGICVC_CTRL_VSYNC_INVERT;
111
112         if (logicvc->interface) {
113                 struct drm_connector *connector =
114                         &logicvc->interface->drm_connector;
115                 struct drm_display_info *display_info =
116                         &connector->display_info;
117
118                 if (display_info->bus_flags & DRM_BUS_FLAG_DE_LOW)
119                         ctrl |= LOGICVC_CTRL_DE_INVERT;
120
121                 if (display_info->bus_flags &
122                     DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
123                         ctrl |= LOGICVC_CTRL_CLOCK_INVERT;
124         }
125
126         regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG,
127                            LOGICVC_CTRL_HSYNC_ENABLE |
128                            LOGICVC_CTRL_HSYNC_INVERT |
129                            LOGICVC_CTRL_VSYNC_ENABLE |
130                            LOGICVC_CTRL_VSYNC_INVERT |
131                            LOGICVC_CTRL_DE_ENABLE |
132                            LOGICVC_CTRL_DE_INVERT |
133                            LOGICVC_CTRL_PIXEL_INVERT |
134                            LOGICVC_CTRL_CLOCK_INVERT, ctrl);
135
136         /* Generate internal state reset. */
137         regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0);
138
139         drm_crtc_vblank_on(drm_crtc);
140
141         /* Register our event after vblank is enabled. */
142         if (drm_crtc->state->event && !old_state->active) {
143                 spin_lock_irqsave(&drm_dev->event_lock, flags);
144                 WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0);
145
146                 crtc->event = drm_crtc->state->event;
147                 drm_crtc->state->event = NULL;
148                 spin_unlock_irqrestore(&drm_dev->event_lock, flags);
149         }
150 }
151
152 static void logicvc_crtc_atomic_disable(struct drm_crtc *drm_crtc,
153                                         struct drm_atomic_state *state)
154 {
155         struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
156         struct drm_device *drm_dev = drm_crtc->dev;
157
158         drm_crtc_vblank_off(drm_crtc);
159
160         /* Disable and clear CRTC bits. */
161         regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG,
162                            LOGICVC_CTRL_HSYNC_ENABLE |
163                            LOGICVC_CTRL_HSYNC_INVERT |
164                            LOGICVC_CTRL_VSYNC_ENABLE |
165                            LOGICVC_CTRL_VSYNC_INVERT |
166                            LOGICVC_CTRL_DE_ENABLE |
167                            LOGICVC_CTRL_DE_INVERT |
168                            LOGICVC_CTRL_PIXEL_INVERT |
169                            LOGICVC_CTRL_CLOCK_INVERT, 0);
170
171         /* Generate internal state reset. */
172         regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0);
173
174         /* Consume any leftover event since vblank is now disabled. */
175         if (drm_crtc->state->event && !drm_crtc->state->active) {
176                 spin_lock_irq(&drm_dev->event_lock);
177
178                 drm_crtc_send_vblank_event(drm_crtc, drm_crtc->state->event);
179                 drm_crtc->state->event = NULL;
180                 spin_unlock_irq(&drm_dev->event_lock);
181         }
182 }
183
184 static const struct drm_crtc_helper_funcs logicvc_crtc_helper_funcs = {
185         .mode_valid             = logicvc_crtc_mode_valid,
186         .atomic_begin           = logicvc_crtc_atomic_begin,
187         .atomic_enable          = logicvc_crtc_atomic_enable,
188         .atomic_disable         = logicvc_crtc_atomic_disable,
189 };
190
191 static int logicvc_crtc_enable_vblank(struct drm_crtc *drm_crtc)
192 {
193         struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
194
195         /* Clear any pending V_SYNC interrupt. */
196         regmap_write_bits(logicvc->regmap, LOGICVC_INT_STAT_REG,
197                           LOGICVC_INT_STAT_V_SYNC, LOGICVC_INT_STAT_V_SYNC);
198
199         /* Unmask V_SYNC interrupt. */
200         regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG,
201                           LOGICVC_INT_MASK_V_SYNC, 0);
202
203         return 0;
204 }
205
206 static void logicvc_crtc_disable_vblank(struct drm_crtc *drm_crtc)
207 {
208         struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
209
210         /* Mask V_SYNC interrupt. */
211         regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG,
212                           LOGICVC_INT_MASK_V_SYNC, LOGICVC_INT_MASK_V_SYNC);
213 }
214
215 static const struct drm_crtc_funcs logicvc_crtc_funcs = {
216         .reset                  = drm_atomic_helper_crtc_reset,
217         .destroy                = drm_crtc_cleanup,
218         .set_config             = drm_atomic_helper_set_config,
219         .page_flip              = drm_atomic_helper_page_flip,
220         .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
221         .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
222         .enable_vblank          = logicvc_crtc_enable_vblank,
223         .disable_vblank         = logicvc_crtc_disable_vblank,
224 };
225
226 void logicvc_crtc_vblank_handler(struct logicvc_drm *logicvc)
227 {
228         struct drm_device *drm_dev = &logicvc->drm_dev;
229         struct logicvc_crtc *crtc = logicvc->crtc;
230         unsigned long flags;
231
232         if (!crtc)
233                 return;
234
235         drm_crtc_handle_vblank(&crtc->drm_crtc);
236
237         if (crtc->event) {
238                 spin_lock_irqsave(&drm_dev->event_lock, flags);
239                 drm_crtc_send_vblank_event(&crtc->drm_crtc, crtc->event);
240                 drm_crtc_vblank_put(&crtc->drm_crtc);
241                 crtc->event = NULL;
242                 spin_unlock_irqrestore(&drm_dev->event_lock, flags);
243         }
244 }
245
246 int logicvc_crtc_init(struct logicvc_drm *logicvc)
247 {
248         struct drm_device *drm_dev = &logicvc->drm_dev;
249         struct device *dev = drm_dev->dev;
250         struct device_node *of_node = dev->of_node;
251         struct logicvc_crtc *crtc;
252         struct logicvc_layer *layer_primary;
253         int ret;
254
255         crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
256         if (!crtc)
257                 return -ENOMEM;
258
259         layer_primary = logicvc_layer_get_primary(logicvc);
260         if (!layer_primary) {
261                 drm_err(drm_dev, "Failed to get primary layer\n");
262                 return -EINVAL;
263         }
264
265         ret = drm_crtc_init_with_planes(drm_dev, &crtc->drm_crtc,
266                                         &layer_primary->drm_plane, NULL,
267                                         &logicvc_crtc_funcs, NULL);
268         if (ret) {
269                 drm_err(drm_dev, "Failed to initialize CRTC\n");
270                 return ret;
271         }
272
273         drm_crtc_helper_add(&crtc->drm_crtc, &logicvc_crtc_helper_funcs);
274
275         crtc->drm_crtc.port = of_graph_get_port_by_id(of_node, 1);
276
277         logicvc->crtc = crtc;
278
279         return 0;
280 }
This page took 0.046404 seconds and 4 git commands to generate.