| /* SPDX-License-Identifier: GPL-2.0+ */ | |
| /* | |
| * Copyright (C) 2023 Loongson Technology Corporation Limited | |
| */ | |
| #ifndef __LSDC_DRV_H__ | |
| #define __LSDC_DRV_H__ | |
| #include <linux/pci.h> | |
| #include <drm/drm_connector.h> | |
| #include <drm/drm_crtc.h> | |
| #include <drm/drm_device.h> | |
| #include <drm/drm_encoder.h> | |
| #include <drm/drm_file.h> | |
| #include <drm/drm_plane.h> | |
| #include <drm/ttm/ttm_device.h> | |
| #include "lsdc_i2c.h" | |
| #include "lsdc_irq.h" | |
| #include "lsdc_gfxpll.h" | |
| #include "lsdc_output.h" | |
| #include "lsdc_pixpll.h" | |
| #include "lsdc_regs.h" | |
| /* Currently, all Loongson display controllers have two display pipes. */ | |
| #define LSDC_NUM_CRTC 2 | |
| /* | |
| * LS7A1000/LS7A2000 chipsets function as the south & north bridges of the | |
| * Loongson 3 series processors, they are equipped with on-board video RAM | |
| * typically. While Loongson LS2K series are low cost SoCs which share the | |
| * system RAM as video RAM, they don't has a dedicated VRAM. | |
| * | |
| * There is only a 1:1 mapping of crtcs, encoders and connectors for the DC | |
| * | |
| * display pipe 0 = crtc0 + dvo0 + encoder0 + connector0 + cursor0 + primary0 | |
| * display pipe 1 = crtc1 + dvo1 + encoder1 + connectro1 + cursor1 + primary1 | |
| */ | |
| enum loongson_chip_id { | |
| CHIP_LS7A1000 = 0, | |
| CHIP_LS7A2000 = 1, | |
| CHIP_LS_LAST, | |
| }; | |
| const struct lsdc_desc * | |
| lsdc_device_probe(struct pci_dev *pdev, enum loongson_chip_id chip); | |
| struct lsdc_kms_funcs; | |
| /* DC specific */ | |
| struct lsdc_desc { | |
| u32 num_of_crtc; | |
| u32 max_pixel_clk; | |
| u32 max_width; | |
| u32 max_height; | |
| u32 num_of_hw_cursor; | |
| u32 hw_cursor_w; | |
| u32 hw_cursor_h; | |
| u32 pitch_align; /* CRTC DMA alignment constraint */ | |
| bool has_vblank_counter; /* 32 bit hw vsync counter */ | |
| /* device dependent ops, dc side */ | |
| const struct lsdc_kms_funcs *funcs; | |
| }; | |
| /* GFX related resources wrangler */ | |
| struct loongson_gfx_desc { | |
| struct lsdc_desc dc; | |
| u32 conf_reg_base; | |
| /* GFXPLL shared by the DC, GMC and GPU */ | |
| struct { | |
| u32 reg_offset; | |
| u32 reg_size; | |
| } gfxpll; | |
| /* Pixel PLL, per display pipe */ | |
| struct { | |
| u32 reg_offset; | |
| u32 reg_size; | |
| } pixpll[LSDC_NUM_CRTC]; | |
| enum loongson_chip_id chip_id; | |
| char model[64]; | |
| }; | |
| static inline const struct loongson_gfx_desc * | |
| to_loongson_gfx(const struct lsdc_desc *dcp) | |
| { | |
| return container_of_const(dcp, struct loongson_gfx_desc, dc); | |
| }; | |
| struct lsdc_reg32 { | |
| char *name; | |
| u32 offset; | |
| }; | |
| /* crtc hardware related ops */ | |
| struct lsdc_crtc; | |
| struct lsdc_crtc_hw_ops { | |
| void (*enable)(struct lsdc_crtc *lcrtc); | |
| void (*disable)(struct lsdc_crtc *lcrtc); | |
| void (*enable_vblank)(struct lsdc_crtc *lcrtc); | |
| void (*disable_vblank)(struct lsdc_crtc *lcrtc); | |
| void (*flip)(struct lsdc_crtc *lcrtc); | |
| void (*clone)(struct lsdc_crtc *lcrtc); | |
| void (*get_scan_pos)(struct lsdc_crtc *lcrtc, int *hpos, int *vpos); | |
| void (*set_mode)(struct lsdc_crtc *lcrtc, const struct drm_display_mode *mode); | |
| void (*soft_reset)(struct lsdc_crtc *lcrtc); | |
| void (*reset)(struct lsdc_crtc *lcrtc); | |
| u32 (*get_vblank_counter)(struct lsdc_crtc *lcrtc); | |
| void (*set_dma_step)(struct lsdc_crtc *lcrtc, enum lsdc_dma_steps step); | |
| }; | |
| struct lsdc_crtc { | |
| struct drm_crtc base; | |
| struct lsdc_pixpll pixpll; | |
| struct lsdc_device *ldev; | |
| const struct lsdc_crtc_hw_ops *hw_ops; | |
| const struct lsdc_reg32 *preg; | |
| unsigned int nreg; | |
| struct drm_info_list *p_info_list; | |
| unsigned int n_info_list; | |
| bool has_vblank; | |
| }; | |
| /* primary plane hardware related ops */ | |
| struct lsdc_primary; | |
| struct lsdc_primary_plane_ops { | |
| void (*update_fb_addr)(struct lsdc_primary *plane, u64 addr); | |
| void (*update_fb_stride)(struct lsdc_primary *plane, u32 stride); | |
| void (*update_fb_format)(struct lsdc_primary *plane, | |
| const struct drm_format_info *format); | |
| }; | |
| struct lsdc_primary { | |
| struct drm_plane base; | |
| const struct lsdc_primary_plane_ops *ops; | |
| struct lsdc_device *ldev; | |
| }; | |
| /* cursor plane hardware related ops */ | |
| struct lsdc_cursor; | |
| struct lsdc_cursor_plane_ops { | |
| void (*update_bo_addr)(struct lsdc_cursor *plane, u64 addr); | |
| void (*update_cfg)(struct lsdc_cursor *plane, | |
| enum lsdc_cursor_size cursor_size, | |
| enum lsdc_cursor_format); | |
| void (*update_position)(struct lsdc_cursor *plane, int x, int y); | |
| }; | |
| struct lsdc_cursor { | |
| struct drm_plane base; | |
| const struct lsdc_cursor_plane_ops *ops; | |
| struct lsdc_device *ldev; | |
| }; | |
| struct lsdc_output { | |
| struct drm_encoder encoder; | |
| struct drm_connector connector; | |
| }; | |
| static inline struct lsdc_output * | |
| connector_to_lsdc_output(struct drm_connector *connector) | |
| { | |
| return container_of(connector, struct lsdc_output, connector); | |
| } | |
| static inline struct lsdc_output * | |
| encoder_to_lsdc_output(struct drm_encoder *encoder) | |
| { | |
| return container_of(encoder, struct lsdc_output, encoder); | |
| } | |
| struct lsdc_display_pipe { | |
| struct lsdc_crtc crtc; | |
| struct lsdc_primary primary; | |
| struct lsdc_cursor cursor; | |
| struct lsdc_output output; | |
| struct lsdc_i2c *li2c; | |
| unsigned int index; | |
| }; | |
| static inline struct lsdc_display_pipe * | |
| output_to_display_pipe(struct lsdc_output *output) | |
| { | |
| return container_of(output, struct lsdc_display_pipe, output); | |
| } | |
| struct lsdc_kms_funcs { | |
| irqreturn_t (*irq_handler)(int irq, void *arg); | |
| int (*create_i2c)(struct drm_device *ddev, | |
| struct lsdc_display_pipe *dispipe, | |
| unsigned int index); | |
| int (*output_init)(struct drm_device *ddev, | |
| struct lsdc_display_pipe *dispipe, | |
| struct i2c_adapter *ddc, | |
| unsigned int index); | |
| int (*cursor_plane_init)(struct drm_device *ddev, | |
| struct drm_plane *plane, | |
| unsigned int index); | |
| int (*primary_plane_init)(struct drm_device *ddev, | |
| struct drm_plane *plane, | |
| unsigned int index); | |
| int (*crtc_init)(struct drm_device *ddev, | |
| struct drm_crtc *crtc, | |
| struct drm_plane *primary, | |
| struct drm_plane *cursor, | |
| unsigned int index, | |
| bool has_vblank); | |
| }; | |
| static inline struct lsdc_crtc * | |
| to_lsdc_crtc(struct drm_crtc *crtc) | |
| { | |
| return container_of(crtc, struct lsdc_crtc, base); | |
| } | |
| static inline struct lsdc_display_pipe * | |
| crtc_to_display_pipe(struct drm_crtc *crtc) | |
| { | |
| return container_of(crtc, struct lsdc_display_pipe, crtc.base); | |
| } | |
| static inline struct lsdc_primary * | |
| to_lsdc_primary(struct drm_plane *plane) | |
| { | |
| return container_of(plane, struct lsdc_primary, base); | |
| } | |
| static inline struct lsdc_cursor * | |
| to_lsdc_cursor(struct drm_plane *plane) | |
| { | |
| return container_of(plane, struct lsdc_cursor, base); | |
| } | |
| struct lsdc_crtc_state { | |
| struct drm_crtc_state base; | |
| struct lsdc_pixpll_parms pparms; | |
| }; | |
| struct lsdc_gem { | |
| /* @mutex: protect objects list */ | |
| struct mutex mutex; | |
| struct list_head objects; | |
| }; | |
| struct lsdc_device { | |
| struct drm_device base; | |
| struct ttm_device bdev; | |
| /* @descp: features description of the DC variant */ | |
| const struct lsdc_desc *descp; | |
| struct pci_dev *dc; | |
| struct pci_dev *gpu; | |
| struct loongson_gfxpll *gfxpll; | |
| /* @reglock: protects concurrent access */ | |
| spinlock_t reglock; | |
| void __iomem *reg_base; | |
| resource_size_t vram_base; | |
| resource_size_t vram_size; | |
| resource_size_t gtt_base; | |
| resource_size_t gtt_size; | |
| struct lsdc_display_pipe dispipe[LSDC_NUM_CRTC]; | |
| struct lsdc_gem gem; | |
| u32 irq_status; | |
| /* tracking pinned memory */ | |
| size_t vram_pinned_size; | |
| size_t gtt_pinned_size; | |
| /* @num_output: count the number of active display pipe */ | |
| unsigned int num_output; | |
| }; | |
| static inline struct lsdc_device *tdev_to_ldev(struct ttm_device *bdev) | |
| { | |
| return container_of(bdev, struct lsdc_device, bdev); | |
| } | |
| static inline struct lsdc_device *to_lsdc(struct drm_device *ddev) | |
| { | |
| return container_of(ddev, struct lsdc_device, base); | |
| } | |
| static inline struct lsdc_crtc_state * | |
| to_lsdc_crtc_state(struct drm_crtc_state *base) | |
| { | |
| return container_of(base, struct lsdc_crtc_state, base); | |
| } | |
| void lsdc_debugfs_init(struct drm_minor *minor); | |
| int ls7a1000_crtc_init(struct drm_device *ddev, | |
| struct drm_crtc *crtc, | |
| struct drm_plane *primary, | |
| struct drm_plane *cursor, | |
| unsigned int index, | |
| bool no_vblank); | |
| int ls7a2000_crtc_init(struct drm_device *ddev, | |
| struct drm_crtc *crtc, | |
| struct drm_plane *primary, | |
| struct drm_plane *cursor, | |
| unsigned int index, | |
| bool no_vblank); | |
| int lsdc_primary_plane_init(struct drm_device *ddev, | |
| struct drm_plane *plane, | |
| unsigned int index); | |
| int ls7a1000_cursor_plane_init(struct drm_device *ddev, | |
| struct drm_plane *plane, | |
| unsigned int index); | |
| int ls7a2000_cursor_plane_init(struct drm_device *ddev, | |
| struct drm_plane *plane, | |
| unsigned int index); | |
| /* Registers access helpers */ | |
| static inline u32 lsdc_rreg32(struct lsdc_device *ldev, u32 offset) | |
| { | |
| return readl(ldev->reg_base + offset); | |
| } | |
| static inline void lsdc_wreg32(struct lsdc_device *ldev, u32 offset, u32 val) | |
| { | |
| writel(val, ldev->reg_base + offset); | |
| } | |
| static inline void lsdc_ureg32_set(struct lsdc_device *ldev, | |
| u32 offset, | |
| u32 mask) | |
| { | |
| void __iomem *addr = ldev->reg_base + offset; | |
| u32 val = readl(addr); | |
| writel(val | mask, addr); | |
| } | |
| static inline void lsdc_ureg32_clr(struct lsdc_device *ldev, | |
| u32 offset, | |
| u32 mask) | |
| { | |
| void __iomem *addr = ldev->reg_base + offset; | |
| u32 val = readl(addr); | |
| writel(val & ~mask, addr); | |
| } | |
| static inline u32 lsdc_pipe_rreg32(struct lsdc_device *ldev, | |
| u32 offset, u32 pipe) | |
| { | |
| return readl(ldev->reg_base + offset + pipe * CRTC_PIPE_OFFSET); | |
| } | |
| static inline void lsdc_pipe_wreg32(struct lsdc_device *ldev, | |
| u32 offset, u32 pipe, u32 val) | |
| { | |
| writel(val, ldev->reg_base + offset + pipe * CRTC_PIPE_OFFSET); | |
| } | |
| #endif |