1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
2 | /* |
3 | * Copyright (C) 2023 Loongson Technology Corporation Limited |
4 | */ |
5 | |
6 | #ifndef __LSDC_DRV_H__ |
7 | #define __LSDC_DRV_H__ |
8 | |
9 | #include <linux/pci.h> |
10 | |
11 | #include <drm/drm_connector.h> |
12 | #include <drm/drm_crtc.h> |
13 | #include <drm/drm_device.h> |
14 | #include <drm/drm_encoder.h> |
15 | #include <drm/drm_file.h> |
16 | #include <drm/drm_plane.h> |
17 | #include <drm/ttm/ttm_device.h> |
18 | |
19 | #include "lsdc_i2c.h" |
20 | #include "lsdc_irq.h" |
21 | #include "lsdc_gfxpll.h" |
22 | #include "lsdc_output.h" |
23 | #include "lsdc_pixpll.h" |
24 | #include "lsdc_regs.h" |
25 | |
26 | /* Currently, all Loongson display controllers have two display pipes. */ |
27 | #define LSDC_NUM_CRTC 2 |
28 | |
29 | /* |
30 | * LS7A1000/LS7A2000 chipsets function as the south & north bridges of the |
31 | * Loongson 3 series processors, they are equipped with on-board video RAM |
32 | * typically. While Loongson LS2K series are low cost SoCs which share the |
33 | * system RAM as video RAM, they don't has a dedicated VRAM. |
34 | * |
35 | * There is only a 1:1 mapping of crtcs, encoders and connectors for the DC |
36 | * |
37 | * display pipe 0 = crtc0 + dvo0 + encoder0 + connector0 + cursor0 + primary0 |
38 | * display pipe 1 = crtc1 + dvo1 + encoder1 + connectro1 + cursor1 + primary1 |
39 | */ |
40 | |
41 | enum loongson_chip_id { |
42 | CHIP_LS7A1000 = 0, |
43 | CHIP_LS7A2000 = 1, |
44 | CHIP_LS_LAST, |
45 | }; |
46 | |
47 | const struct lsdc_desc * |
48 | lsdc_device_probe(struct pci_dev *pdev, enum loongson_chip_id chip); |
49 | |
50 | struct lsdc_kms_funcs; |
51 | |
52 | /* DC specific */ |
53 | |
54 | struct lsdc_desc { |
55 | u32 num_of_crtc; |
56 | u32 max_pixel_clk; |
57 | u32 max_width; |
58 | u32 max_height; |
59 | u32 num_of_hw_cursor; |
60 | u32 hw_cursor_w; |
61 | u32 hw_cursor_h; |
62 | u32 pitch_align; /* CRTC DMA alignment constraint */ |
63 | bool has_vblank_counter; /* 32 bit hw vsync counter */ |
64 | |
65 | /* device dependent ops, dc side */ |
66 | const struct lsdc_kms_funcs *funcs; |
67 | }; |
68 | |
69 | /* GFX related resources wrangler */ |
70 | |
71 | struct loongson_gfx_desc { |
72 | struct lsdc_desc dc; |
73 | |
74 | u32 conf_reg_base; |
75 | |
76 | /* GFXPLL shared by the DC, GMC and GPU */ |
77 | struct { |
78 | u32 reg_offset; |
79 | u32 reg_size; |
80 | } gfxpll; |
81 | |
82 | /* Pixel PLL, per display pipe */ |
83 | struct { |
84 | u32 reg_offset; |
85 | u32 reg_size; |
86 | } pixpll[LSDC_NUM_CRTC]; |
87 | |
88 | enum loongson_chip_id chip_id; |
89 | char model[64]; |
90 | }; |
91 | |
92 | static inline const struct loongson_gfx_desc * |
93 | to_loongson_gfx(const struct lsdc_desc *dcp) |
94 | { |
95 | return container_of_const(dcp, struct loongson_gfx_desc, dc); |
96 | }; |
97 | |
98 | struct lsdc_reg32 { |
99 | char *name; |
100 | u32 offset; |
101 | }; |
102 | |
103 | /* crtc hardware related ops */ |
104 | |
105 | struct lsdc_crtc; |
106 | |
107 | struct lsdc_crtc_hw_ops { |
108 | void (*enable)(struct lsdc_crtc *lcrtc); |
109 | void (*disable)(struct lsdc_crtc *lcrtc); |
110 | void (*enable_vblank)(struct lsdc_crtc *lcrtc); |
111 | void (*disable_vblank)(struct lsdc_crtc *lcrtc); |
112 | void (*flip)(struct lsdc_crtc *lcrtc); |
113 | void (*clone)(struct lsdc_crtc *lcrtc); |
114 | void (*get_scan_pos)(struct lsdc_crtc *lcrtc, int *hpos, int *vpos); |
115 | void (*set_mode)(struct lsdc_crtc *lcrtc, const struct drm_display_mode *mode); |
116 | void (*soft_reset)(struct lsdc_crtc *lcrtc); |
117 | void (*reset)(struct lsdc_crtc *lcrtc); |
118 | |
119 | u32 (*get_vblank_counter)(struct lsdc_crtc *lcrtc); |
120 | void (*set_dma_step)(struct lsdc_crtc *lcrtc, enum lsdc_dma_steps step); |
121 | }; |
122 | |
123 | struct lsdc_crtc { |
124 | struct drm_crtc base; |
125 | struct lsdc_pixpll pixpll; |
126 | struct lsdc_device *ldev; |
127 | const struct lsdc_crtc_hw_ops *hw_ops; |
128 | const struct lsdc_reg32 *preg; |
129 | unsigned int nreg; |
130 | struct drm_info_list *p_info_list; |
131 | unsigned int n_info_list; |
132 | bool has_vblank; |
133 | }; |
134 | |
135 | /* primary plane hardware related ops */ |
136 | |
137 | struct lsdc_primary; |
138 | |
139 | struct lsdc_primary_plane_ops { |
140 | void (*update_fb_addr)(struct lsdc_primary *plane, u64 addr); |
141 | void (*update_fb_stride)(struct lsdc_primary *plane, u32 stride); |
142 | void (*update_fb_format)(struct lsdc_primary *plane, |
143 | const struct drm_format_info *format); |
144 | }; |
145 | |
146 | struct lsdc_primary { |
147 | struct drm_plane base; |
148 | const struct lsdc_primary_plane_ops *ops; |
149 | struct lsdc_device *ldev; |
150 | }; |
151 | |
152 | /* cursor plane hardware related ops */ |
153 | |
154 | struct lsdc_cursor; |
155 | |
156 | struct lsdc_cursor_plane_ops { |
157 | void (*update_bo_addr)(struct lsdc_cursor *plane, u64 addr); |
158 | void (*update_cfg)(struct lsdc_cursor *plane, |
159 | enum lsdc_cursor_size cursor_size, |
160 | enum lsdc_cursor_format); |
161 | void (*update_position)(struct lsdc_cursor *plane, int x, int y); |
162 | }; |
163 | |
164 | struct lsdc_cursor { |
165 | struct drm_plane base; |
166 | const struct lsdc_cursor_plane_ops *ops; |
167 | struct lsdc_device *ldev; |
168 | }; |
169 | |
170 | struct lsdc_output { |
171 | struct drm_encoder encoder; |
172 | struct drm_connector connector; |
173 | }; |
174 | |
175 | static inline struct lsdc_output * |
176 | connector_to_lsdc_output(struct drm_connector *connector) |
177 | { |
178 | return container_of(connector, struct lsdc_output, connector); |
179 | } |
180 | |
181 | static inline struct lsdc_output * |
182 | encoder_to_lsdc_output(struct drm_encoder *encoder) |
183 | { |
184 | return container_of(encoder, struct lsdc_output, encoder); |
185 | } |
186 | |
187 | struct lsdc_display_pipe { |
188 | struct lsdc_crtc crtc; |
189 | struct lsdc_primary primary; |
190 | struct lsdc_cursor cursor; |
191 | struct lsdc_output output; |
192 | struct lsdc_i2c *li2c; |
193 | unsigned int index; |
194 | }; |
195 | |
196 | static inline struct lsdc_display_pipe * |
197 | output_to_display_pipe(struct lsdc_output *output) |
198 | { |
199 | return container_of(output, struct lsdc_display_pipe, output); |
200 | } |
201 | |
202 | struct lsdc_kms_funcs { |
203 | irqreturn_t (*irq_handler)(int irq, void *arg); |
204 | |
205 | int (*create_i2c)(struct drm_device *ddev, |
206 | struct lsdc_display_pipe *dispipe, |
207 | unsigned int index); |
208 | |
209 | int (*output_init)(struct drm_device *ddev, |
210 | struct lsdc_display_pipe *dispipe, |
211 | struct i2c_adapter *ddc, |
212 | unsigned int index); |
213 | |
214 | int (*cursor_plane_init)(struct drm_device *ddev, |
215 | struct drm_plane *plane, |
216 | unsigned int index); |
217 | |
218 | int (*primary_plane_init)(struct drm_device *ddev, |
219 | struct drm_plane *plane, |
220 | unsigned int index); |
221 | |
222 | int (*crtc_init)(struct drm_device *ddev, |
223 | struct drm_crtc *crtc, |
224 | struct drm_plane *primary, |
225 | struct drm_plane *cursor, |
226 | unsigned int index, |
227 | bool has_vblank); |
228 | }; |
229 | |
230 | static inline struct lsdc_crtc * |
231 | to_lsdc_crtc(struct drm_crtc *crtc) |
232 | { |
233 | return container_of(crtc, struct lsdc_crtc, base); |
234 | } |
235 | |
236 | static inline struct lsdc_display_pipe * |
237 | crtc_to_display_pipe(struct drm_crtc *crtc) |
238 | { |
239 | return container_of(crtc, struct lsdc_display_pipe, crtc.base); |
240 | } |
241 | |
242 | static inline struct lsdc_primary * |
243 | to_lsdc_primary(struct drm_plane *plane) |
244 | { |
245 | return container_of(plane, struct lsdc_primary, base); |
246 | } |
247 | |
248 | static inline struct lsdc_cursor * |
249 | to_lsdc_cursor(struct drm_plane *plane) |
250 | { |
251 | return container_of(plane, struct lsdc_cursor, base); |
252 | } |
253 | |
254 | struct lsdc_crtc_state { |
255 | struct drm_crtc_state base; |
256 | struct lsdc_pixpll_parms pparms; |
257 | }; |
258 | |
259 | struct lsdc_gem { |
260 | /* @mutex: protect objects list */ |
261 | struct mutex mutex; |
262 | struct list_head objects; |
263 | }; |
264 | |
265 | struct lsdc_device { |
266 | struct drm_device base; |
267 | struct ttm_device bdev; |
268 | |
269 | /* @descp: features description of the DC variant */ |
270 | const struct lsdc_desc *descp; |
271 | struct pci_dev *dc; |
272 | struct pci_dev *gpu; |
273 | |
274 | struct loongson_gfxpll *gfxpll; |
275 | |
276 | /* @reglock: protects concurrent access */ |
277 | spinlock_t reglock; |
278 | |
279 | void __iomem *reg_base; |
280 | resource_size_t vram_base; |
281 | resource_size_t vram_size; |
282 | |
283 | resource_size_t gtt_base; |
284 | resource_size_t gtt_size; |
285 | |
286 | struct lsdc_display_pipe dispipe[LSDC_NUM_CRTC]; |
287 | |
288 | struct lsdc_gem gem; |
289 | |
290 | u32 irq_status; |
291 | |
292 | /* tracking pinned memory */ |
293 | size_t vram_pinned_size; |
294 | size_t gtt_pinned_size; |
295 | |
296 | /* @num_output: count the number of active display pipe */ |
297 | unsigned int num_output; |
298 | }; |
299 | |
300 | static inline struct lsdc_device *tdev_to_ldev(struct ttm_device *bdev) |
301 | { |
302 | return container_of(bdev, struct lsdc_device, bdev); |
303 | } |
304 | |
305 | static inline struct lsdc_device *to_lsdc(struct drm_device *ddev) |
306 | { |
307 | return container_of(ddev, struct lsdc_device, base); |
308 | } |
309 | |
310 | static inline struct lsdc_crtc_state * |
311 | to_lsdc_crtc_state(struct drm_crtc_state *base) |
312 | { |
313 | return container_of(base, struct lsdc_crtc_state, base); |
314 | } |
315 | |
316 | void lsdc_debugfs_init(struct drm_minor *minor); |
317 | |
318 | int ls7a1000_crtc_init(struct drm_device *ddev, |
319 | struct drm_crtc *crtc, |
320 | struct drm_plane *primary, |
321 | struct drm_plane *cursor, |
322 | unsigned int index, |
323 | bool no_vblank); |
324 | |
325 | int ls7a2000_crtc_init(struct drm_device *ddev, |
326 | struct drm_crtc *crtc, |
327 | struct drm_plane *primary, |
328 | struct drm_plane *cursor, |
329 | unsigned int index, |
330 | bool no_vblank); |
331 | |
332 | int lsdc_primary_plane_init(struct drm_device *ddev, |
333 | struct drm_plane *plane, |
334 | unsigned int index); |
335 | |
336 | int ls7a1000_cursor_plane_init(struct drm_device *ddev, |
337 | struct drm_plane *plane, |
338 | unsigned int index); |
339 | |
340 | int ls7a2000_cursor_plane_init(struct drm_device *ddev, |
341 | struct drm_plane *plane, |
342 | unsigned int index); |
343 | |
344 | /* Registers access helpers */ |
345 | |
346 | static inline u32 lsdc_rreg32(struct lsdc_device *ldev, u32 offset) |
347 | { |
348 | return readl(addr: ldev->reg_base + offset); |
349 | } |
350 | |
351 | static inline void lsdc_wreg32(struct lsdc_device *ldev, u32 offset, u32 val) |
352 | { |
353 | writel(val, addr: ldev->reg_base + offset); |
354 | } |
355 | |
356 | static inline void lsdc_ureg32_set(struct lsdc_device *ldev, |
357 | u32 offset, |
358 | u32 mask) |
359 | { |
360 | void __iomem *addr = ldev->reg_base + offset; |
361 | u32 val = readl(addr); |
362 | |
363 | writel(val: val | mask, addr); |
364 | } |
365 | |
366 | static inline void lsdc_ureg32_clr(struct lsdc_device *ldev, |
367 | u32 offset, |
368 | u32 mask) |
369 | { |
370 | void __iomem *addr = ldev->reg_base + offset; |
371 | u32 val = readl(addr); |
372 | |
373 | writel(val: val & ~mask, addr); |
374 | } |
375 | |
376 | static inline u32 lsdc_pipe_rreg32(struct lsdc_device *ldev, |
377 | u32 offset, u32 pipe) |
378 | { |
379 | return readl(addr: ldev->reg_base + offset + pipe * CRTC_PIPE_OFFSET); |
380 | } |
381 | |
382 | static inline void lsdc_pipe_wreg32(struct lsdc_device *ldev, |
383 | u32 offset, u32 pipe, u32 val) |
384 | { |
385 | writel(val, addr: ldev->reg_base + offset + pipe * CRTC_PIPE_OFFSET); |
386 | } |
387 | |
388 | #endif |
389 | |