/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2023 Loongson Technology Corporation Limited */ #ifndef __LSDC_DRV_H__ #define __LSDC_DRV_H__ #include #include #include #include #include #include #include #include #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