Message ID | 20190328054316.17939-3-joel@jms.id.au |
---|---|
State | Superseded |
Headers | show |
Series | drm: Add ASPEED BMC 'GFX' driver | expand |
On Thu, Mar 28, 2019 at 04:13:16PM +1030, Joel Stanley wrote: > This driver is for the ASPEED BMC SoC's GFX display hardware. This > driver runs on the ARM based BMC systems, unlike the ast driver which > runs on a host CPU and is is for a PCI graphics device. > > Signed-off-by: Joel Stanley <joel@jms.id.au> > -- > Changes since RFC: > drm_fbdev_cma_init -> drm_fb_cma_fbdev_init and use generic lastclose callback > Use generic irq handling instead of drm_irq_install > Add doc to driver > Get rid of unncessary reads in irq enable/disable path > Rebase on linux-next > > drivers/gpu/drm/Kconfig | 2 + > drivers/gpu/drm/Makefile | 1 + > drivers/gpu/drm/aspeed/Kconfig | 15 ++ > drivers/gpu/drm/aspeed/Makefile | 3 + > drivers/gpu/drm/aspeed/aspeed_gfx.h | 104 +++++++++ > drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c | 248 +++++++++++++++++++++ > drivers/gpu/drm/aspeed/aspeed_gfx_drv.c | 269 +++++++++++++++++++++++ > drivers/gpu/drm/aspeed/aspeed_gfx_out.c | 49 +++++ > 8 files changed, 691 insertions(+) > create mode 100644 drivers/gpu/drm/aspeed/Kconfig > create mode 100644 drivers/gpu/drm/aspeed/Makefile > create mode 100644 drivers/gpu/drm/aspeed/aspeed_gfx.h > create mode 100644 drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c > create mode 100644 drivers/gpu/drm/aspeed/aspeed_gfx_drv.c > create mode 100644 drivers/gpu/drm/aspeed/aspeed_gfx_out.c > > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > index 82bb221ec94e..b1ec8f85c2a8 100644 > --- a/drivers/gpu/drm/Kconfig > +++ b/drivers/gpu/drm/Kconfig > @@ -335,6 +335,8 @@ source "drivers/gpu/drm/xen/Kconfig" > > source "drivers/gpu/drm/vboxvideo/Kconfig" > > +source "drivers/gpu/drm/aspeed/Kconfig" > + > # Keep legacy drivers last > > menuconfig DRM_LEGACY > diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile > index 0baf148e3687..df8835045310 100644 > --- a/drivers/gpu/drm/Makefile > +++ b/drivers/gpu/drm/Makefile > @@ -110,3 +110,4 @@ obj-$(CONFIG_DRM_PL111) += pl111/ > obj-$(CONFIG_DRM_TVE200) += tve200/ > obj-$(CONFIG_DRM_XEN) += xen/ > obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/ > +obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/ > diff --git a/drivers/gpu/drm/aspeed/Kconfig b/drivers/gpu/drm/aspeed/Kconfig > new file mode 100644 > index 000000000000..6f1e64c0a6ce > --- /dev/null > +++ b/drivers/gpu/drm/aspeed/Kconfig > @@ -0,0 +1,15 @@ > +config DRM_ASPEED_GFX > + tristate "ASPEED BMC Display Controller" > + depends on DRM && OF > + select DRM_KMS_HELPER > + select DRM_KMS_FB_HELPER > + select DRM_KMS_CMA_HELPER > + select DRM_PANEL > + select DMA_CMA > + select CMA > + select MFD_SYSCON > + help > + Chose this option if you have an ASPEED AST2400/AST2500 > + SOC Display Controller (aka GFX). > + > + If M is selected this module will be called aspeed_gfx. > diff --git a/drivers/gpu/drm/aspeed/Makefile b/drivers/gpu/drm/aspeed/Makefile > new file mode 100644 > index 000000000000..6e194cd790d8 > --- /dev/null > +++ b/drivers/gpu/drm/aspeed/Makefile > @@ -0,0 +1,3 @@ > +aspeed_gfx-y := aspeed_gfx_drv.o aspeed_gfx_crtc.o aspeed_gfx_out.o > + > +obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed_gfx.o > diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed_gfx.h > new file mode 100644 > index 000000000000..fb56e425bd48 > --- /dev/null > +++ b/drivers/gpu/drm/aspeed/aspeed_gfx.h > @@ -0,0 +1,104 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +// Copyright 2018 IBM Corporation > + > +#include <drm/drmP.h> > +#include <drm/drm_simple_kms_helper.h> > + > +struct aspeed_gfx { > + void __iomem *base; > + struct clk *clk; > + struct reset_control *rst; > + struct regmap *scu; > + > + struct drm_simple_display_pipe pipe; > + struct drm_connector connector; > + struct drm_fbdev_cma *fbdev; > +}; > + > +int aspeed_gfx_create_pipe(struct drm_device *drm); > +int aspeed_gfx_create_output(struct drm_device *drm); > + > +#define CRT_CTRL1 0x60 /* CRT Control I */ > +#define CRT_CTRL2 0x64 /* CRT Control II */ > +#define CRT_STATUS 0x68 /* CRT Status */ > +#define CRT_MISC 0x6c /* CRT Misc Setting */ > +#define CRT_HORIZ0 0x70 /* CRT Horizontal Total & Display Enable End */ > +#define CRT_HORIZ1 0x74 /* CRT Horizontal Retrace Start & End */ > +#define CRT_VERT0 0x78 /* CRT Vertical Total & Display Enable End */ > +#define CRT_VERT1 0x7C /* CRT Vertical Retrace Start & End */ > +#define CRT_ADDR 0x80 /* CRT Display Starting Address */ > +#define CRT_OFFSET 0x84 /* CRT Display Offset & Terminal Count */ > +#define CRT_THROD 0x88 /* CRT Threshold */ > +#define CRT_XSCALE 0x8C /* CRT Scaling-Up Factor */ > +#define CRT_CURSOR0 0x90 /* CRT Hardware Cursor X & Y Offset */ > +#define CRT_CURSOR1 0x94 /* CRT Hardware Cursor X & Y Position */ > +#define CRT_CURSOR2 0x98 /* CRT Hardware Cursor Pattern Address */ > +#define CRT_9C 0x9C > +#define CRT_OSD_H 0xA0 /* CRT OSD Horizontal Start/End */ > +#define CRT_OSD_V 0xA4 /* CRT OSD Vertical Start/End */ > +#define CRT_OSD_ADDR 0xA8 /* CRT OSD Pattern Address */ > +#define CRT_OSD_DISP 0xAC /* CRT OSD Offset */ > +#define CRT_OSD_THRESH 0xB0 /* CRT OSD Threshold & Alpha */ > +#define CRT_B4 0xB4 > +#define CRT_STS_V 0xB8 /* CRT Status V */ > +#define CRT_SCRATCH 0xBC /* Scratchpad */ > +#define CRT_BB0_ADDR 0xD0 /* CRT Display BB0 Starting Address */ > +#define CRT_BB1_ADDR 0xD4 /* CRT Display BB1 Starting Address */ > +#define CRT_BB_COUNT 0xD8 /* CRT Display BB Terminal Count */ > +#define OSD_COLOR1 0xE0 /* OSD Color Palette Index 1 & 0 */ > +#define OSD_COLOR2 0xE4 /* OSD Color Palette Index 3 & 2 */ > +#define OSD_COLOR3 0xE8 /* OSD Color Palette Index 5 & 4 */ > +#define OSD_COLOR4 0xEC /* OSD Color Palette Index 7 & 6 */ > +#define OSD_COLOR5 0xF0 /* OSD Color Palette Index 9 & 8 */ > +#define OSD_COLOR6 0xF4 /* OSD Color Palette Index 11 & 10 */ > +#define OSD_COLOR7 0xF8 /* OSD Color Palette Index 13 & 12 */ > +#define OSD_COLOR8 0xFC /* OSD Color Palette Index 15 & 14 */ > + > +/* CTRL1 */ > +#define CRT_CTRL_EN BIT(0) > +#define CRT_CTRL_HW_CURSOR_EN BIT(1) > +#define CRT_CTRL_OSD_EN BIT(2) > +#define CRT_CTRL_INTERLACED BIT(3) > +#define CRT_CTRL_COLOR_RGB565 (0 << 7) > +#define CRT_CTRL_COLOR_YUV444 (1 << 7) > +#define CRT_CTRL_COLOR_XRGB8888 (2 << 7) > +#define CRT_CTRL_COLOR_RGB888 (3 << 7) > +#define CRT_CTRL_COLOR_YUV444_2RGB (5 << 7) > +#define CRT_CTRL_COLOR_YUV422 (7 << 7) > +#define CRT_CTRL_COLOR_MASK GENMASK(9, 7) > +#define CRT_CTRL_HSYNC_NEGATIVE BIT(16) > +#define CRT_CTRL_VSYNC_NEGATIVE BIT(17) > +#define CRT_CTRL_VERTICAL_INTR_EN BIT(30) > +#define CRT_CTRL_VERTICAL_INTR_STS BIT(31) > + > +/* CTRL2 */ > +#define CRT_CTRL_DAC_EN BIT(0) > +#define CRT_CTRL_VBLANK_LINE(x) (((x) << 20) & CRT_CTRL_VBLANK_LINE_MASK) > +#define CRT_CTRL_VBLANK_LINE_MASK GENMASK(20, 31) > + > +/* CRT_HORIZ0 */ > +#define CRT_H_TOTAL(x) (x) > +#define CRT_H_DE(x) ((x) << 16) > + > +/* CRT_HORIZ1 */ > +#define CRT_H_RS_START(x) (x) > +#define CRT_H_RS_END(x) ((x) << 16) > + > +/* CRT_VIRT0 */ > +#define CRT_V_TOTAL(x) (x) > +#define CRT_V_DE(x) ((x) << 16) > + > +/* CRT_VIRT1 */ > +#define CRT_V_RS_START(x) (x) > +#define CRT_V_RS_END(x) ((x) << 16) > + > +/* CRT_OFFSET */ > +#define CRT_DISP_OFFSET(x) (x) > +#define CRT_TERM_COUNT(x) ((x) << 16) > + > +/* CRT_THROD */ > +#define CRT_THROD_LOW(x) (x) > +#define CRT_THROD_HIGH(x) ((x) << 8) > + > +/* Default Threshold Seting */ > +#define G5_CRT_THROD_VAL (CRT_THROD_LOW(0x24) | CRT_THROD_HIGH(0x3C)) > diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c > new file mode 100644 > index 000000000000..e2d1d7497352 > --- /dev/null > +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c > @@ -0,0 +1,248 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +// Copyright 2018 IBM Corporation > + > +#include <linux/clk.h> > +#include <linux/reset.h> > +#include <linux/regmap.h> > + > +#include <drm/drm_crtc_helper.h> > +#include <drm/drm_device.h> > +#include <drm/drm_fb_cma_helper.h> > +#include <drm/drm_gem_cma_helper.h> > +#include <drm/drm_simple_kms_helper.h> > +#include <drm/drm_gem_framebuffer_helper.h> > +#include <drm/drm_panel.h> > + > +#include "aspeed_gfx.h" > + > +static struct aspeed_gfx * > +drm_pipe_to_aspeed_gfx(struct drm_simple_display_pipe *pipe) > +{ > + return container_of(pipe, struct aspeed_gfx, pipe); > +} > + > +static int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp) > +{ > + struct drm_crtc *crtc = &priv->pipe.crtc; > + struct drm_device *drm = crtc->dev; > + const u32 format = crtc->primary->state->fb->format->format; > + u32 ctrl1; > + > + ctrl1 = readl(priv->base + CRT_CTRL1); > + ctrl1 &= ~CRT_CTRL_COLOR_MASK; > + > + switch (format) { > + case DRM_FORMAT_RGB565: > + dev_dbg(drm->dev, "Setting up RGB565 mode\n"); > + ctrl1 |= CRT_CTRL_COLOR_RGB565; > + *bpp = 16; > + break; > + case DRM_FORMAT_XRGB8888: > + dev_dbg(drm->dev, "Setting up XRGB8888 mode\n"); > + ctrl1 |= CRT_CTRL_COLOR_XRGB8888; > + *bpp = 32; > + break; > + default: > + dev_err(drm->dev, "Unhandled pixel format %08x\n", format); > + return -EINVAL; > + } > + > + writel(ctrl1, priv->base + CRT_CTRL1); > + > + return 0; > +} > + > +static void aspeed_gfx_enable_controller(struct aspeed_gfx *priv) > +{ > + u32 ctrl1 = readl(priv->base + CRT_CTRL1); > + u32 ctrl2 = readl(priv->base + CRT_CTRL2); > + > + /* SCU2C: set DAC source for display output to Graphics CRT (GFX) */ > + regmap_update_bits(priv->scu, 0x2c, BIT(16), BIT(16)); > + > + writel(ctrl1 | CRT_CTRL_EN, priv->base + CRT_CTRL1); > + writel(ctrl2 | CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2); > +} > + > +static void aspeed_gfx_disable_controller(struct aspeed_gfx *priv) > +{ > + u32 ctrl1 = readl(priv->base + CRT_CTRL1); > + u32 ctrl2 = readl(priv->base + CRT_CTRL2); > + > + writel(ctrl1 & ~CRT_CTRL_EN, priv->base + CRT_CTRL1); > + writel(ctrl2 & ~CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2); > + > + regmap_update_bits(priv->scu, 0x2c, BIT(16), 0); > +} > + > +static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv) > +{ > + struct drm_display_mode *m = &priv->pipe.crtc.state->adjusted_mode; > + u32 ctrl1, d_offset, t_count, bpp; > + int err; > + > + err = aspeed_gfx_set_pixel_fmt(priv, &bpp); > + if (err) > + return; > + > +#if 0 > + /* TODO: we have only been able to test with the 40MHz USB clock. The > + * clock is fixed, so we cannot adjust it here. */ > + clk_set_rate(priv->pixel_clk, m->crtc_clock * 1000); > +#endif > + > + ctrl1 = readl(priv->base + CRT_CTRL1); > + ctrl1 &= ~(CRT_CTRL_INTERLACED | > + CRT_CTRL_HSYNC_NEGATIVE | > + CRT_CTRL_VSYNC_NEGATIVE); > + > + if (m->flags & DRM_MODE_FLAG_INTERLACE) > + ctrl1 |= CRT_CTRL_INTERLACED; > + > + if (!(m->flags & DRM_MODE_FLAG_PHSYNC)) > + ctrl1 |= CRT_CTRL_HSYNC_NEGATIVE; > + > + if (!(m->flags & DRM_MODE_FLAG_PVSYNC)) > + ctrl1 |= CRT_CTRL_VSYNC_NEGATIVE; > + > + writel(ctrl1, priv->base + CRT_CTRL1); > + > + /* Horizontal timing */ > + writel(CRT_H_TOTAL(m->htotal - 1) | CRT_H_DE(m->hdisplay - 1), > + priv->base + CRT_HORIZ0); > + writel(CRT_H_RS_START(m->hsync_start - 1) | CRT_H_RS_END(m->hsync_end), > + priv->base + CRT_HORIZ1); > + > + > + /* Vertical timing */ > + writel(CRT_V_TOTAL(m->vtotal - 1) | CRT_V_DE(m->vdisplay - 1), > + priv->base + CRT_VERT0); > + writel(CRT_V_RS_START(m->vsync_start) | CRT_V_RS_END(m->vsync_end), > + priv->base + CRT_VERT1); > + > + /* > + * Display Offset: address difference between consecutive scan lines > + * Terminal Count: memory size of one scan line > + */ > + d_offset = m->hdisplay * bpp / 8; > + t_count = (m->hdisplay * bpp + 127) / 128; > + writel(CRT_DISP_OFFSET(d_offset) | CRT_TERM_COUNT(t_count), > + priv->base + CRT_OFFSET); > + > + /* > + * Threshold: FIFO thresholds of refill and stop (16 byte chunks > + * per line, rounded up) > + */ > + writel(G5_CRT_THROD_VAL, priv->base + CRT_THROD); > +} > + > +static void aspeed_gfx_pipe_enable(struct drm_simple_display_pipe *pipe, > + struct drm_crtc_state *crtc_state, > + struct drm_plane_state *plane_state) > +{ > + struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); > + struct drm_crtc *crtc = &pipe->crtc; > + > + aspeed_gfx_crtc_mode_set_nofb(priv); > + aspeed_gfx_enable_controller(priv); > + drm_crtc_vblank_on(crtc); > +} > + > +static void aspeed_gfx_pipe_disable(struct drm_simple_display_pipe *pipe) > +{ > + struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); > + struct drm_crtc *crtc = &pipe->crtc; > + > + drm_crtc_vblank_off(crtc); > + aspeed_gfx_disable_controller(priv); > +} > + > +static void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe, > + struct drm_plane_state *plane_state) > +{ > + struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); > + struct drm_crtc *crtc = &pipe->crtc; > + struct drm_framebuffer *fb = pipe->plane.state->fb; > + struct drm_pending_vblank_event *event; > + struct drm_gem_cma_object *gem; > + > + if (!crtc) > + return; > + > + spin_lock_irq(&crtc->dev->event_lock); > + event = crtc->state->event; > + if (event) { > + crtc->state->event = NULL; > + > + if (drm_crtc_vblank_get(crtc) == 0) > + drm_crtc_arm_vblank_event(crtc, event); > + else > + drm_crtc_send_vblank_event(crtc, event); > + } > + spin_unlock_irq(&crtc->dev->event_lock); > + > + if (!fb) > + return; > + > + gem = drm_fb_cma_get_gem_obj(fb, 0); > + if (!gem) > + return; > + writel(gem->paddr, priv->base + CRT_ADDR); > +} > + > +static int aspeed_gfx_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, > + struct drm_plane_state *plane_state) > +{ > + return drm_gem_fb_prepare_fb(&pipe->plane, plane_state); > +} drm_gem_fb_simple_display_pipe_prepare_fb > + > +static int aspeed_gfx_enable_vblank(struct drm_simple_display_pipe *pipe) > +{ > + struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); > + u32 reg = readl(priv->base + CRT_CTRL1); > + > + /* Clear pending VBLANK IRQ */ > + writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1); > + > + reg |= CRT_CTRL_VERTICAL_INTR_EN; > + writel(reg, priv->base + CRT_CTRL1); > + > + return 0; > +} > + > +static void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe) > +{ > + struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); > + u32 reg = readl(priv->base + CRT_CTRL1); > + > + reg &= ~CRT_CTRL_VERTICAL_INTR_EN; > + writel(reg, priv->base + CRT_CTRL1); > + > + /* Clear pending VBLANK IRQ */ > + writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1); > +} > + > +static struct drm_simple_display_pipe_funcs aspeed_gfx_funcs = { > + .enable = aspeed_gfx_pipe_enable, > + .disable = aspeed_gfx_pipe_disable, > + .update = aspeed_gfx_pipe_update, > + .prepare_fb = aspeed_gfx_pipe_prepare_fb, > + .enable_vblank = aspeed_gfx_enable_vblank, > + .disable_vblank = aspeed_gfx_disable_vblank, > +}; > + > +static const uint32_t aspeed_gfx_formats[] = { > + DRM_FORMAT_XRGB8888, > + DRM_FORMAT_RGB565, > +}; > + > +int aspeed_gfx_create_pipe(struct drm_device *drm) > +{ > + struct aspeed_gfx *priv = drm->dev_private; > + > + return drm_simple_display_pipe_init(drm, &priv->pipe, &aspeed_gfx_funcs, > + aspeed_gfx_formats, > + ARRAY_SIZE(aspeed_gfx_formats), > + NULL, > + &priv->connector); > +} > diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c > new file mode 100644 > index 000000000000..6b88d658ac1f > --- /dev/null > +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c > @@ -0,0 +1,269 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +// Copyright 2018 IBM Corporation > + > +#include <linux/module.h> > +#include <linux/irq.h> > +#include <linux/clk.h> > +#include <linux/of.h> > +#include <linux/of_reserved_mem.h> > +#include <linux/reset.h> > +#include <linux/regmap.h> > +#include <linux/mfd/syscon.h> > + > +#include <drm/drm_atomic_helper.h> > +#include <drm/drm_crtc_helper.h> > +#include <drm/drm_device.h> > +#include <drm/drm_fb_cma_helper.h> > +#include <drm/drm_fb_helper.h> > +#include <drm/drm_gem_cma_helper.h> > +#include <drm/drm_gem_framebuffer_helper.h> > +#include <drm/drm_simple_kms_helper.h> > +#include <drm/drm_probe_helper.h> > + > +#include "aspeed_gfx.h" > + > +/** > + * DOC: ASPEED GFX Driver > + * > + * This driver is for the ASPEED BMC SoC's GFX display hardware. This > + * driver runs on the ARM based BMC systems, unlike the ast driver which > + * runs on a host CPU and is is for a PCI graphics device. > + * > + * The AST2500 supports a total of 3 output paths: > + * > + * 1. VGA output, the output target can choose either or both to the DAC > + * or DVO interface. > + * > + * 2. Graphics CRT output, the output target can choose either or both to > + * the DAC or DVO interface. > + * > + * 3. Video input from DVO, the video input can be used for video engine > + * capture or DAC display output. > + * > + * Output options are selected in SCU2C. > + * > + * The "VGA mode" device is the PCI attached controller. The "Graphics CRT" > + * is the ARM's internal display controller. > + * > + * The driver only supports a simple configuration consisting of a 40MHz > + * pixel clock, fixed by hardware limitations, and the VGA output path. > + */ > + > +static const struct drm_mode_config_funcs aspeed_gfx_mode_config_funcs = { > + .fb_create = drm_gem_fb_create, > + .atomic_check = drm_atomic_helper_check, > + .atomic_commit = drm_atomic_helper_commit, > +}; > + > +static void aspeed_gfx_setup_mode_config(struct drm_device *drm) > +{ > + drm_mode_config_init(drm); > + > + drm->mode_config.min_width = 0; > + drm->mode_config.min_height = 0; > + drm->mode_config.max_width = 800; > + drm->mode_config.max_height = 600; > + drm->mode_config.funcs = &aspeed_gfx_mode_config_funcs; > +} > + > +static irqreturn_t aspeed_gfx_irq_handler(int irq, void *data) > +{ > + struct drm_device *drm = data; > + struct aspeed_gfx *priv = drm->dev_private; > + u32 reg; > + > + reg = readl(priv->base + CRT_CTRL1); > + > + if (reg & CRT_CTRL_VERTICAL_INTR_STS) { > + drm_crtc_handle_vblank(&priv->pipe.crtc); > + writel(reg, priv->base + CRT_CTRL1); > + return IRQ_HANDLED; > + } > + > + return IRQ_NONE; > +} > + > + > + > +static int aspeed_gfx_load(struct drm_device *drm) > +{ > + struct platform_device *pdev = to_platform_device(drm->dev); > + struct aspeed_gfx *priv; > + struct resource *res; > + int ret; > + > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + drm->dev_private = priv; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + priv->base = devm_ioremap_resource(drm->dev, res); > + if (IS_ERR(priv->base)) > + return PTR_ERR(priv->base); > + > + priv->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu"); > + if (IS_ERR(priv->scu)) { > + dev_err(&pdev->dev, "failed to find SCU regmap\n"); > + return PTR_ERR(priv->scu); > + } > + > + ret = of_reserved_mem_device_init(drm->dev); > + if (ret) { > + dev_err(&pdev->dev, > + "failed to initialize reserved mem: %d\n", ret); > + return ret; > + } > + > + ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32)); > + if (ret) { > + dev_err(&pdev->dev, "failed to set DMA mask: %d\n", ret); > + return ret; > + } > + > + priv->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); > + if (IS_ERR(priv->rst)) { > + dev_err(&pdev->dev, > + "missing or invalid reset controller device tree entry"); > + return PTR_ERR(priv->rst); > + } > + reset_control_deassert(priv->rst); > + > + priv->clk = devm_clk_get(drm->dev, NULL); > + if (IS_ERR(priv->clk)) { > + dev_err(&pdev->dev, > + "missing or invalid clk device tree entry"); > + return PTR_ERR(priv->clk); > + } > + clk_prepare_enable(priv->clk); > + > + /* Sanitize control registers */ > + writel(0, priv->base + CRT_CTRL1); > + writel(0, priv->base + CRT_CTRL2); > + > + aspeed_gfx_setup_mode_config(drm); > + > + ret = drm_vblank_init(drm, 1); > + if (ret < 0) { > + dev_err(drm->dev, "Failed to initialise vblank\n"); > + return ret; > + } > + > + ret = aspeed_gfx_create_output(drm); > + if (ret < 0) { > + dev_err(drm->dev, "Failed to create outputs\n"); > + return ret; > + } > + > + ret = aspeed_gfx_create_pipe(drm); > + if (ret < 0) { > + dev_err(drm->dev, "Cannot setup simple display pipe\n"); > + return ret; > + } > + > + ret = devm_request_irq(drm->dev, platform_get_irq(pdev, 0), > + aspeed_gfx_irq_handler, 0, "aspeed gfx", drm); > + if (ret < 0) { > + dev_err(drm->dev, "Failed to install IRQ handler\n"); > + return ret; > + } > + > + drm_mode_config_reset(drm); > + > + drm_fbdev_generic_setup(drm, 32); > + > + return 0; > +} > + > +static void aspeed_gfx_unload(struct drm_device *drm) > +{ > + drm_kms_helper_poll_fini(drm); > + drm_mode_config_cleanup(drm); > + > + drm->dev_private = NULL; > +} > + > +DEFINE_DRM_GEM_CMA_FOPS(fops); > + > +static struct drm_driver aspeed_gfx_driver = { > + .driver_features = DRIVER_GEM | DRIVER_MODESET | > + DRIVER_PRIME | DRIVER_ATOMIC | > + DRIVER_HAVE_IRQ, > + .gem_free_object_unlocked = drm_gem_cma_free_object, > + .gem_vm_ops = &drm_gem_cma_vm_ops, > + .dumb_create = drm_gem_cma_dumb_create, > + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, > + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, > + .gem_prime_export = drm_gem_prime_export, > + .gem_prime_import = drm_gem_prime_import, > + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, > + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, > + .gem_prime_vmap = drm_gem_cma_prime_vmap, > + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, > + .gem_prime_mmap = drm_gem_cma_prime_mmap, > + .fops = &fops, > + .name = "aspeed-gfx-drm", > + .desc = "ASPEED GFX DRM", > + .date = "20180319", > + .major = 1, > + .minor = 0, > +}; > + > +static const struct of_device_id aspeed_gfx_match[] = { > + { .compatible = "aspeed,ast2400-gfx" }, > + { .compatible = "aspeed,ast2500-gfx" }, > + { } > +}; > + > +static int aspeed_gfx_probe(struct platform_device *pdev) > +{ > + struct drm_device *drm; > + int ret; > + > + drm = drm_dev_alloc(&aspeed_gfx_driver, &pdev->dev); > + if (IS_ERR(drm)) > + return PTR_ERR(drm); > + > + ret = aspeed_gfx_load(drm); > + if (ret) > + goto err_free; > + > + ret = drm_dev_register(drm, 0); > + if (ret) > + goto err_unload; > + > + return 0; > + > +err_unload: > + aspeed_gfx_unload(drm); > +err_free: > + drm_dev_put(drm); > + > + return ret; > +} > + > +static int aspeed_gfx_remove(struct platform_device *pdev) > +{ > + struct drm_device *drm = platform_get_drvdata(pdev); > + > + drm_dev_unregister(drm); > + aspeed_gfx_unload(drm); > + drm_dev_put(drm); > + > + return 0; > +} > + > +static struct platform_driver aspeed_gfx_platform_driver = { > + .probe = aspeed_gfx_probe, > + .remove = aspeed_gfx_remove, > + .driver = { > + .name = "aspeed_gfx", > + .of_match_table = aspeed_gfx_match, > + }, > +}; > + > +module_platform_driver(aspeed_gfx_platform_driver); > + > +MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>"); > +MODULE_DESCRIPTION("ASPEED BMC DRM/KMS driver"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_out.c b/drivers/gpu/drm/aspeed/aspeed_gfx_out.c > new file mode 100644 > index 000000000000..7d2057e00056 > --- /dev/null > +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_out.c > @@ -0,0 +1,49 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +// Copyright 2018 IBM Corporation > + > +#include <drm/drmP.h> > +#include <drm/drm_atomic_helper.h> > +#include <drm/drm_connector.h> > +#include <drm/drm_crtc_helper.h> > +#include <drm/drm_probe_helper.h> > + > +#include "aspeed_gfx.h" > + > +static int aspeed_gfx_get_modes(struct drm_connector *connector) > +{ > + return drm_add_modes_noedid(connector, 800, 600); Is this the only mode you do, or just a default? Iirc if you report "connected", you'll get this as one of the fallback modes already. > +} > + > +static const struct > +drm_connector_helper_funcs aspeed_gfx_connector_helper_funcs = { > + .get_modes = aspeed_gfx_get_modes, > +}; > + > +static void aspeed_gfx_connector_destroy(struct drm_connector *connector) > +{ > + drm_connector_unregister(connector); Only needed for hotunplugged connectors, drm_dev_unregister takes care of this for you already. > + drm_connector_cleanup(connector); > +} > + > +static const struct drm_connector_funcs aspeed_gfx_connector_funcs = { > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = aspeed_gfx_connector_destroy, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > +}; > + > +int aspeed_gfx_create_output(struct drm_device *drm) > +{ > + struct aspeed_gfx *priv = drm->dev_private; > + int ret; > + > + priv->connector.dpms = DRM_MODE_DPMS_OFF; > + priv->connector.polled = 0; > + drm_connector_helper_add(&priv->connector, > + &aspeed_gfx_connector_helper_funcs); > + ret = drm_connector_init(drm, &priv->connector, > + &aspeed_gfx_connector_funcs, > + DRM_MODE_CONNECTOR_Unknown); > + return ret; > +} > -- > 2.20.1 Very nice driver! Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> For merging/maintenance, do you want to put this into drm-misc? https://drm.pages.freedesktop.org/maintainer-tools/committer-drm-misc.html#small-drivers We could also officially put drm/ast in there, if you want to maintain that too. Either way, patch also needs a MAINTAINERS entry. -Daniel -- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
Hi Joel. Replying to Noralf's mail as I lost the original mail. > > diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c > > new file mode 100644 > > index 000000000000..e2d1d7497352 > > --- /dev/null > > +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c > > @@ -0,0 +1,248 @@ > > +// SPDX-License-Identifier: GPL-2.0+ > > +// Copyright 2018 IBM Corporation > > + > > +#include <linux/clk.h> > > +#include <linux/reset.h> > > +#include <linux/regmap.h> > > + > > +#include <drm/drm_crtc_helper.h> > > +#include <drm/drm_device.h> > > +#include <drm/drm_fb_cma_helper.h> > > +#include <drm/drm_gem_cma_helper.h> > > +#include <drm/drm_simple_kms_helper.h> > > +#include <drm/drm_gem_framebuffer_helper.h> > > +#include <drm/drm_panel.h> > > I prefer includes sorted alphabetically, but no requirement. Please sort them as Noralf suggest, as this makes it much more obvious when one is adding a duplicate header. > > + > > + priv->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); > > + if (IS_ERR(priv->rst)) { > > + dev_err(&pdev->dev, > > + "missing or invalid reset controller device tree entry"); Add error code to your dev_err() calls, so one later better can tell what was wrong if river fails to load. Same goes for other dev_xxx calls in this function / dirver. (Most dev_xxx have the return code, only a few seems to miss it) > > +static const struct of_device_id aspeed_gfx_match[] = { > > + { .compatible = "aspeed,ast2400-gfx" }, > > + { .compatible = "aspeed,ast2500-gfx" }, > > + { } Many drivers put a /* sentinel */ comment inside the empty {} With the few things suggested above considered this is Reviewed-by: Sam Ravnborg <sam@ravnborg.org> Sam
On Thu, 28 Mar 2019 at 07:53, Daniel Vetter <daniel@ffwll.ch> wrote: > > +static int aspeed_gfx_get_modes(struct drm_connector *connector) > > +{ > > + return drm_add_modes_noedid(connector, 800, 600); > > Is this the only mode you do, or just a default? Iirc if you report > "connected", you'll get this as one of the fallback modes already. I this is the only mode we can do with the clock that we have available. > Very nice driver! > > Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> Thanks! > For merging/maintenance, do you want to put this into drm-misc? > > https://drm.pages.freedesktop.org/maintainer-tools/committer-drm-misc.html#small-drivers That makes sense. I'll send out a v2 with the changes you and Noralf suggested, and a maintainers patch, once I've given it a spin on hardware. > We could also officially put drm/ast in there, if you want to maintain > that too. I haven't had much to do with that driver (it's physically inside the BMC, but a different piece of hardware). I can probably be included so I'm on cc for changes. Cheers, Joel > > Either way, patch also needs a MAINTAINERS entry. > -Daniel > -- > Daniel Vetter > Software Engineer, Intel Corporation > http://blog.ffwll.ch > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 82bb221ec94e..b1ec8f85c2a8 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -335,6 +335,8 @@ source "drivers/gpu/drm/xen/Kconfig" source "drivers/gpu/drm/vboxvideo/Kconfig" +source "drivers/gpu/drm/aspeed/Kconfig" + # Keep legacy drivers last menuconfig DRM_LEGACY diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 0baf148e3687..df8835045310 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -110,3 +110,4 @@ obj-$(CONFIG_DRM_PL111) += pl111/ obj-$(CONFIG_DRM_TVE200) += tve200/ obj-$(CONFIG_DRM_XEN) += xen/ obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/ +obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/ diff --git a/drivers/gpu/drm/aspeed/Kconfig b/drivers/gpu/drm/aspeed/Kconfig new file mode 100644 index 000000000000..6f1e64c0a6ce --- /dev/null +++ b/drivers/gpu/drm/aspeed/Kconfig @@ -0,0 +1,15 @@ +config DRM_ASPEED_GFX + tristate "ASPEED BMC Display Controller" + depends on DRM && OF + select DRM_KMS_HELPER + select DRM_KMS_FB_HELPER + select DRM_KMS_CMA_HELPER + select DRM_PANEL + select DMA_CMA + select CMA + select MFD_SYSCON + help + Chose this option if you have an ASPEED AST2400/AST2500 + SOC Display Controller (aka GFX). + + If M is selected this module will be called aspeed_gfx. diff --git a/drivers/gpu/drm/aspeed/Makefile b/drivers/gpu/drm/aspeed/Makefile new file mode 100644 index 000000000000..6e194cd790d8 --- /dev/null +++ b/drivers/gpu/drm/aspeed/Makefile @@ -0,0 +1,3 @@ +aspeed_gfx-y := aspeed_gfx_drv.o aspeed_gfx_crtc.o aspeed_gfx_out.o + +obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed_gfx.o diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed_gfx.h new file mode 100644 index 000000000000..fb56e425bd48 --- /dev/null +++ b/drivers/gpu/drm/aspeed/aspeed_gfx.h @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2018 IBM Corporation + +#include <drm/drmP.h> +#include <drm/drm_simple_kms_helper.h> + +struct aspeed_gfx { + void __iomem *base; + struct clk *clk; + struct reset_control *rst; + struct regmap *scu; + + struct drm_simple_display_pipe pipe; + struct drm_connector connector; + struct drm_fbdev_cma *fbdev; +}; + +int aspeed_gfx_create_pipe(struct drm_device *drm); +int aspeed_gfx_create_output(struct drm_device *drm); + +#define CRT_CTRL1 0x60 /* CRT Control I */ +#define CRT_CTRL2 0x64 /* CRT Control II */ +#define CRT_STATUS 0x68 /* CRT Status */ +#define CRT_MISC 0x6c /* CRT Misc Setting */ +#define CRT_HORIZ0 0x70 /* CRT Horizontal Total & Display Enable End */ +#define CRT_HORIZ1 0x74 /* CRT Horizontal Retrace Start & End */ +#define CRT_VERT0 0x78 /* CRT Vertical Total & Display Enable End */ +#define CRT_VERT1 0x7C /* CRT Vertical Retrace Start & End */ +#define CRT_ADDR 0x80 /* CRT Display Starting Address */ +#define CRT_OFFSET 0x84 /* CRT Display Offset & Terminal Count */ +#define CRT_THROD 0x88 /* CRT Threshold */ +#define CRT_XSCALE 0x8C /* CRT Scaling-Up Factor */ +#define CRT_CURSOR0 0x90 /* CRT Hardware Cursor X & Y Offset */ +#define CRT_CURSOR1 0x94 /* CRT Hardware Cursor X & Y Position */ +#define CRT_CURSOR2 0x98 /* CRT Hardware Cursor Pattern Address */ +#define CRT_9C 0x9C +#define CRT_OSD_H 0xA0 /* CRT OSD Horizontal Start/End */ +#define CRT_OSD_V 0xA4 /* CRT OSD Vertical Start/End */ +#define CRT_OSD_ADDR 0xA8 /* CRT OSD Pattern Address */ +#define CRT_OSD_DISP 0xAC /* CRT OSD Offset */ +#define CRT_OSD_THRESH 0xB0 /* CRT OSD Threshold & Alpha */ +#define CRT_B4 0xB4 +#define CRT_STS_V 0xB8 /* CRT Status V */ +#define CRT_SCRATCH 0xBC /* Scratchpad */ +#define CRT_BB0_ADDR 0xD0 /* CRT Display BB0 Starting Address */ +#define CRT_BB1_ADDR 0xD4 /* CRT Display BB1 Starting Address */ +#define CRT_BB_COUNT 0xD8 /* CRT Display BB Terminal Count */ +#define OSD_COLOR1 0xE0 /* OSD Color Palette Index 1 & 0 */ +#define OSD_COLOR2 0xE4 /* OSD Color Palette Index 3 & 2 */ +#define OSD_COLOR3 0xE8 /* OSD Color Palette Index 5 & 4 */ +#define OSD_COLOR4 0xEC /* OSD Color Palette Index 7 & 6 */ +#define OSD_COLOR5 0xF0 /* OSD Color Palette Index 9 & 8 */ +#define OSD_COLOR6 0xF4 /* OSD Color Palette Index 11 & 10 */ +#define OSD_COLOR7 0xF8 /* OSD Color Palette Index 13 & 12 */ +#define OSD_COLOR8 0xFC /* OSD Color Palette Index 15 & 14 */ + +/* CTRL1 */ +#define CRT_CTRL_EN BIT(0) +#define CRT_CTRL_HW_CURSOR_EN BIT(1) +#define CRT_CTRL_OSD_EN BIT(2) +#define CRT_CTRL_INTERLACED BIT(3) +#define CRT_CTRL_COLOR_RGB565 (0 << 7) +#define CRT_CTRL_COLOR_YUV444 (1 << 7) +#define CRT_CTRL_COLOR_XRGB8888 (2 << 7) +#define CRT_CTRL_COLOR_RGB888 (3 << 7) +#define CRT_CTRL_COLOR_YUV444_2RGB (5 << 7) +#define CRT_CTRL_COLOR_YUV422 (7 << 7) +#define CRT_CTRL_COLOR_MASK GENMASK(9, 7) +#define CRT_CTRL_HSYNC_NEGATIVE BIT(16) +#define CRT_CTRL_VSYNC_NEGATIVE BIT(17) +#define CRT_CTRL_VERTICAL_INTR_EN BIT(30) +#define CRT_CTRL_VERTICAL_INTR_STS BIT(31) + +/* CTRL2 */ +#define CRT_CTRL_DAC_EN BIT(0) +#define CRT_CTRL_VBLANK_LINE(x) (((x) << 20) & CRT_CTRL_VBLANK_LINE_MASK) +#define CRT_CTRL_VBLANK_LINE_MASK GENMASK(20, 31) + +/* CRT_HORIZ0 */ +#define CRT_H_TOTAL(x) (x) +#define CRT_H_DE(x) ((x) << 16) + +/* CRT_HORIZ1 */ +#define CRT_H_RS_START(x) (x) +#define CRT_H_RS_END(x) ((x) << 16) + +/* CRT_VIRT0 */ +#define CRT_V_TOTAL(x) (x) +#define CRT_V_DE(x) ((x) << 16) + +/* CRT_VIRT1 */ +#define CRT_V_RS_START(x) (x) +#define CRT_V_RS_END(x) ((x) << 16) + +/* CRT_OFFSET */ +#define CRT_DISP_OFFSET(x) (x) +#define CRT_TERM_COUNT(x) ((x) << 16) + +/* CRT_THROD */ +#define CRT_THROD_LOW(x) (x) +#define CRT_THROD_HIGH(x) ((x) << 8) + +/* Default Threshold Seting */ +#define G5_CRT_THROD_VAL (CRT_THROD_LOW(0x24) | CRT_THROD_HIGH(0x3C)) diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c new file mode 100644 index 000000000000..e2d1d7497352 --- /dev/null +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2018 IBM Corporation + +#include <linux/clk.h> +#include <linux/reset.h> +#include <linux/regmap.h> + +#include <drm/drm_crtc_helper.h> +#include <drm/drm_device.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_simple_kms_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_panel.h> + +#include "aspeed_gfx.h" + +static struct aspeed_gfx * +drm_pipe_to_aspeed_gfx(struct drm_simple_display_pipe *pipe) +{ + return container_of(pipe, struct aspeed_gfx, pipe); +} + +static int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp) +{ + struct drm_crtc *crtc = &priv->pipe.crtc; + struct drm_device *drm = crtc->dev; + const u32 format = crtc->primary->state->fb->format->format; + u32 ctrl1; + + ctrl1 = readl(priv->base + CRT_CTRL1); + ctrl1 &= ~CRT_CTRL_COLOR_MASK; + + switch (format) { + case DRM_FORMAT_RGB565: + dev_dbg(drm->dev, "Setting up RGB565 mode\n"); + ctrl1 |= CRT_CTRL_COLOR_RGB565; + *bpp = 16; + break; + case DRM_FORMAT_XRGB8888: + dev_dbg(drm->dev, "Setting up XRGB8888 mode\n"); + ctrl1 |= CRT_CTRL_COLOR_XRGB8888; + *bpp = 32; + break; + default: + dev_err(drm->dev, "Unhandled pixel format %08x\n", format); + return -EINVAL; + } + + writel(ctrl1, priv->base + CRT_CTRL1); + + return 0; +} + +static void aspeed_gfx_enable_controller(struct aspeed_gfx *priv) +{ + u32 ctrl1 = readl(priv->base + CRT_CTRL1); + u32 ctrl2 = readl(priv->base + CRT_CTRL2); + + /* SCU2C: set DAC source for display output to Graphics CRT (GFX) */ + regmap_update_bits(priv->scu, 0x2c, BIT(16), BIT(16)); + + writel(ctrl1 | CRT_CTRL_EN, priv->base + CRT_CTRL1); + writel(ctrl2 | CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2); +} + +static void aspeed_gfx_disable_controller(struct aspeed_gfx *priv) +{ + u32 ctrl1 = readl(priv->base + CRT_CTRL1); + u32 ctrl2 = readl(priv->base + CRT_CTRL2); + + writel(ctrl1 & ~CRT_CTRL_EN, priv->base + CRT_CTRL1); + writel(ctrl2 & ~CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2); + + regmap_update_bits(priv->scu, 0x2c, BIT(16), 0); +} + +static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv) +{ + struct drm_display_mode *m = &priv->pipe.crtc.state->adjusted_mode; + u32 ctrl1, d_offset, t_count, bpp; + int err; + + err = aspeed_gfx_set_pixel_fmt(priv, &bpp); + if (err) + return; + +#if 0 + /* TODO: we have only been able to test with the 40MHz USB clock. The + * clock is fixed, so we cannot adjust it here. */ + clk_set_rate(priv->pixel_clk, m->crtc_clock * 1000); +#endif + + ctrl1 = readl(priv->base + CRT_CTRL1); + ctrl1 &= ~(CRT_CTRL_INTERLACED | + CRT_CTRL_HSYNC_NEGATIVE | + CRT_CTRL_VSYNC_NEGATIVE); + + if (m->flags & DRM_MODE_FLAG_INTERLACE) + ctrl1 |= CRT_CTRL_INTERLACED; + + if (!(m->flags & DRM_MODE_FLAG_PHSYNC)) + ctrl1 |= CRT_CTRL_HSYNC_NEGATIVE; + + if (!(m->flags & DRM_MODE_FLAG_PVSYNC)) + ctrl1 |= CRT_CTRL_VSYNC_NEGATIVE; + + writel(ctrl1, priv->base + CRT_CTRL1); + + /* Horizontal timing */ + writel(CRT_H_TOTAL(m->htotal - 1) | CRT_H_DE(m->hdisplay - 1), + priv->base + CRT_HORIZ0); + writel(CRT_H_RS_START(m->hsync_start - 1) | CRT_H_RS_END(m->hsync_end), + priv->base + CRT_HORIZ1); + + + /* Vertical timing */ + writel(CRT_V_TOTAL(m->vtotal - 1) | CRT_V_DE(m->vdisplay - 1), + priv->base + CRT_VERT0); + writel(CRT_V_RS_START(m->vsync_start) | CRT_V_RS_END(m->vsync_end), + priv->base + CRT_VERT1); + + /* + * Display Offset: address difference between consecutive scan lines + * Terminal Count: memory size of one scan line + */ + d_offset = m->hdisplay * bpp / 8; + t_count = (m->hdisplay * bpp + 127) / 128; + writel(CRT_DISP_OFFSET(d_offset) | CRT_TERM_COUNT(t_count), + priv->base + CRT_OFFSET); + + /* + * Threshold: FIFO thresholds of refill and stop (16 byte chunks + * per line, rounded up) + */ + writel(G5_CRT_THROD_VAL, priv->base + CRT_THROD); +} + +static void aspeed_gfx_pipe_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) +{ + struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); + struct drm_crtc *crtc = &pipe->crtc; + + aspeed_gfx_crtc_mode_set_nofb(priv); + aspeed_gfx_enable_controller(priv); + drm_crtc_vblank_on(crtc); +} + +static void aspeed_gfx_pipe_disable(struct drm_simple_display_pipe *pipe) +{ + struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); + struct drm_crtc *crtc = &pipe->crtc; + + drm_crtc_vblank_off(crtc); + aspeed_gfx_disable_controller(priv); +} + +static void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state) +{ + struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); + struct drm_crtc *crtc = &pipe->crtc; + struct drm_framebuffer *fb = pipe->plane.state->fb; + struct drm_pending_vblank_event *event; + struct drm_gem_cma_object *gem; + + if (!crtc) + return; + + spin_lock_irq(&crtc->dev->event_lock); + event = crtc->state->event; + if (event) { + crtc->state->event = NULL; + + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + } + spin_unlock_irq(&crtc->dev->event_lock); + + if (!fb) + return; + + gem = drm_fb_cma_get_gem_obj(fb, 0); + if (!gem) + return; + writel(gem->paddr, priv->base + CRT_ADDR); +} + +static int aspeed_gfx_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state) +{ + return drm_gem_fb_prepare_fb(&pipe->plane, plane_state); +} + +static int aspeed_gfx_enable_vblank(struct drm_simple_display_pipe *pipe) +{ + struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); + u32 reg = readl(priv->base + CRT_CTRL1); + + /* Clear pending VBLANK IRQ */ + writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1); + + reg |= CRT_CTRL_VERTICAL_INTR_EN; + writel(reg, priv->base + CRT_CTRL1); + + return 0; +} + +static void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe) +{ + struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); + u32 reg = readl(priv->base + CRT_CTRL1); + + reg &= ~CRT_CTRL_VERTICAL_INTR_EN; + writel(reg, priv->base + CRT_CTRL1); + + /* Clear pending VBLANK IRQ */ + writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1); +} + +static struct drm_simple_display_pipe_funcs aspeed_gfx_funcs = { + .enable = aspeed_gfx_pipe_enable, + .disable = aspeed_gfx_pipe_disable, + .update = aspeed_gfx_pipe_update, + .prepare_fb = aspeed_gfx_pipe_prepare_fb, + .enable_vblank = aspeed_gfx_enable_vblank, + .disable_vblank = aspeed_gfx_disable_vblank, +}; + +static const uint32_t aspeed_gfx_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, +}; + +int aspeed_gfx_create_pipe(struct drm_device *drm) +{ + struct aspeed_gfx *priv = drm->dev_private; + + return drm_simple_display_pipe_init(drm, &priv->pipe, &aspeed_gfx_funcs, + aspeed_gfx_formats, + ARRAY_SIZE(aspeed_gfx_formats), + NULL, + &priv->connector); +} diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c new file mode 100644 index 000000000000..6b88d658ac1f --- /dev/null +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2018 IBM Corporation + +#include <linux/module.h> +#include <linux/irq.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_reserved_mem.h> +#include <linux/reset.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_device.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_simple_kms_helper.h> +#include <drm/drm_probe_helper.h> + +#include "aspeed_gfx.h" + +/** + * DOC: ASPEED GFX Driver + * + * This driver is for the ASPEED BMC SoC's GFX display hardware. This + * driver runs on the ARM based BMC systems, unlike the ast driver which + * runs on a host CPU and is is for a PCI graphics device. + * + * The AST2500 supports a total of 3 output paths: + * + * 1. VGA output, the output target can choose either or both to the DAC + * or DVO interface. + * + * 2. Graphics CRT output, the output target can choose either or both to + * the DAC or DVO interface. + * + * 3. Video input from DVO, the video input can be used for video engine + * capture or DAC display output. + * + * Output options are selected in SCU2C. + * + * The "VGA mode" device is the PCI attached controller. The "Graphics CRT" + * is the ARM's internal display controller. + * + * The driver only supports a simple configuration consisting of a 40MHz + * pixel clock, fixed by hardware limitations, and the VGA output path. + */ + +static const struct drm_mode_config_funcs aspeed_gfx_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static void aspeed_gfx_setup_mode_config(struct drm_device *drm) +{ + drm_mode_config_init(drm); + + drm->mode_config.min_width = 0; + drm->mode_config.min_height = 0; + drm->mode_config.max_width = 800; + drm->mode_config.max_height = 600; + drm->mode_config.funcs = &aspeed_gfx_mode_config_funcs; +} + +static irqreturn_t aspeed_gfx_irq_handler(int irq, void *data) +{ + struct drm_device *drm = data; + struct aspeed_gfx *priv = drm->dev_private; + u32 reg; + + reg = readl(priv->base + CRT_CTRL1); + + if (reg & CRT_CTRL_VERTICAL_INTR_STS) { + drm_crtc_handle_vblank(&priv->pipe.crtc); + writel(reg, priv->base + CRT_CTRL1); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + + + +static int aspeed_gfx_load(struct drm_device *drm) +{ + struct platform_device *pdev = to_platform_device(drm->dev); + struct aspeed_gfx *priv; + struct resource *res; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + drm->dev_private = priv; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(drm->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu"); + if (IS_ERR(priv->scu)) { + dev_err(&pdev->dev, "failed to find SCU regmap\n"); + return PTR_ERR(priv->scu); + } + + ret = of_reserved_mem_device_init(drm->dev); + if (ret) { + dev_err(&pdev->dev, + "failed to initialize reserved mem: %d\n", ret); + return ret; + } + + ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "failed to set DMA mask: %d\n", ret); + return ret; + } + + priv->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(priv->rst)) { + dev_err(&pdev->dev, + "missing or invalid reset controller device tree entry"); + return PTR_ERR(priv->rst); + } + reset_control_deassert(priv->rst); + + priv->clk = devm_clk_get(drm->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, + "missing or invalid clk device tree entry"); + return PTR_ERR(priv->clk); + } + clk_prepare_enable(priv->clk); + + /* Sanitize control registers */ + writel(0, priv->base + CRT_CTRL1); + writel(0, priv->base + CRT_CTRL2); + + aspeed_gfx_setup_mode_config(drm); + + ret = drm_vblank_init(drm, 1); + if (ret < 0) { + dev_err(drm->dev, "Failed to initialise vblank\n"); + return ret; + } + + ret = aspeed_gfx_create_output(drm); + if (ret < 0) { + dev_err(drm->dev, "Failed to create outputs\n"); + return ret; + } + + ret = aspeed_gfx_create_pipe(drm); + if (ret < 0) { + dev_err(drm->dev, "Cannot setup simple display pipe\n"); + return ret; + } + + ret = devm_request_irq(drm->dev, platform_get_irq(pdev, 0), + aspeed_gfx_irq_handler, 0, "aspeed gfx", drm); + if (ret < 0) { + dev_err(drm->dev, "Failed to install IRQ handler\n"); + return ret; + } + + drm_mode_config_reset(drm); + + drm_fbdev_generic_setup(drm, 32); + + return 0; +} + +static void aspeed_gfx_unload(struct drm_device *drm) +{ + drm_kms_helper_poll_fini(drm); + drm_mode_config_cleanup(drm); + + drm->dev_private = NULL; +} + +DEFINE_DRM_GEM_CMA_FOPS(fops); + +static struct drm_driver aspeed_gfx_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | + DRIVER_PRIME | DRIVER_ATOMIC | + DRIVER_HAVE_IRQ, + .gem_free_object_unlocked = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = drm_gem_cma_dumb_create, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + .fops = &fops, + .name = "aspeed-gfx-drm", + .desc = "ASPEED GFX DRM", + .date = "20180319", + .major = 1, + .minor = 0, +}; + +static const struct of_device_id aspeed_gfx_match[] = { + { .compatible = "aspeed,ast2400-gfx" }, + { .compatible = "aspeed,ast2500-gfx" }, + { } +}; + +static int aspeed_gfx_probe(struct platform_device *pdev) +{ + struct drm_device *drm; + int ret; + + drm = drm_dev_alloc(&aspeed_gfx_driver, &pdev->dev); + if (IS_ERR(drm)) + return PTR_ERR(drm); + + ret = aspeed_gfx_load(drm); + if (ret) + goto err_free; + + ret = drm_dev_register(drm, 0); + if (ret) + goto err_unload; + + return 0; + +err_unload: + aspeed_gfx_unload(drm); +err_free: + drm_dev_put(drm); + + return ret; +} + +static int aspeed_gfx_remove(struct platform_device *pdev) +{ + struct drm_device *drm = platform_get_drvdata(pdev); + + drm_dev_unregister(drm); + aspeed_gfx_unload(drm); + drm_dev_put(drm); + + return 0; +} + +static struct platform_driver aspeed_gfx_platform_driver = { + .probe = aspeed_gfx_probe, + .remove = aspeed_gfx_remove, + .driver = { + .name = "aspeed_gfx", + .of_match_table = aspeed_gfx_match, + }, +}; + +module_platform_driver(aspeed_gfx_platform_driver); + +MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>"); +MODULE_DESCRIPTION("ASPEED BMC DRM/KMS driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_out.c b/drivers/gpu/drm/aspeed/aspeed_gfx_out.c new file mode 100644 index 000000000000..7d2057e00056 --- /dev/null +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_out.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2018 IBM Corporation + +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_probe_helper.h> + +#include "aspeed_gfx.h" + +static int aspeed_gfx_get_modes(struct drm_connector *connector) +{ + return drm_add_modes_noedid(connector, 800, 600); +} + +static const struct +drm_connector_helper_funcs aspeed_gfx_connector_helper_funcs = { + .get_modes = aspeed_gfx_get_modes, +}; + +static void aspeed_gfx_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs aspeed_gfx_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = aspeed_gfx_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +int aspeed_gfx_create_output(struct drm_device *drm) +{ + struct aspeed_gfx *priv = drm->dev_private; + int ret; + + priv->connector.dpms = DRM_MODE_DPMS_OFF; + priv->connector.polled = 0; + drm_connector_helper_add(&priv->connector, + &aspeed_gfx_connector_helper_funcs); + ret = drm_connector_init(drm, &priv->connector, + &aspeed_gfx_connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + return ret; +}
This driver is for the ASPEED BMC SoC's GFX display hardware. This driver runs on the ARM based BMC systems, unlike the ast driver which runs on a host CPU and is is for a PCI graphics device. Signed-off-by: Joel Stanley <joel@jms.id.au> -- Changes since RFC: drm_fbdev_cma_init -> drm_fb_cma_fbdev_init and use generic lastclose callback Use generic irq handling instead of drm_irq_install Add doc to driver Get rid of unncessary reads in irq enable/disable path Rebase on linux-next drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/aspeed/Kconfig | 15 ++ drivers/gpu/drm/aspeed/Makefile | 3 + drivers/gpu/drm/aspeed/aspeed_gfx.h | 104 +++++++++ drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c | 248 +++++++++++++++++++++ drivers/gpu/drm/aspeed/aspeed_gfx_drv.c | 269 +++++++++++++++++++++++ drivers/gpu/drm/aspeed/aspeed_gfx_out.c | 49 +++++ 8 files changed, 691 insertions(+) create mode 100644 drivers/gpu/drm/aspeed/Kconfig create mode 100644 drivers/gpu/drm/aspeed/Makefile create mode 100644 drivers/gpu/drm/aspeed/aspeed_gfx.h create mode 100644 drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c create mode 100644 drivers/gpu/drm/aspeed/aspeed_gfx_drv.c create mode 100644 drivers/gpu/drm/aspeed/aspeed_gfx_out.c -- 2.20.1