Skip to content

Commit

Permalink
drm: Add support of ARC PGU display controller
Browse files Browse the repository at this point in the history
ARC PGU could be found on some development boards from Synopsys.
This is a simple byte streamer that reads data from a framebuffer
and sends data to the single encoder.

Signed-off-by: Carlos Palminha <palminha@synopsys.com>
Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
Cc: David Airlie <airlied@linux.ie>
Cc: dri-devel@lists.freedesktop.org
Cc: linux-snps-arc@lists.infradead.org
  • Loading branch information
Carlos Palminha authored and Alexey Brodkin committed Apr 26, 2016
1 parent 027b3f8 commit 51dacf2
Show file tree
Hide file tree
Showing 9 changed files with 845 additions and 0 deletions.
2 changes: 2 additions & 0 deletions drivers/gpu/drm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,5 @@ source "drivers/gpu/drm/imx/Kconfig"
source "drivers/gpu/drm/vc4/Kconfig"

source "drivers/gpu/drm/etnaviv/Kconfig"

source "drivers/gpu/drm/arc/Kconfig"
1 change: 1 addition & 0 deletions drivers/gpu/drm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,4 @@ obj-y += panel/
obj-y += bridge/
obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
obj-$(CONFIG_DRM_ARCPGU)+= arc/
10 changes: 10 additions & 0 deletions drivers/gpu/drm/arc/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
config DRM_ARCPGU
tristate "ARC PGU"
depends on DRM && OF
select DRM_KMS_CMA_HELPER
select DRM_KMS_FB_HELPER
select DRM_KMS_HELPER
help
Choose this option if you have an ARC PGU controller.

If M is selected the module will be called arcpgu.
2 changes: 2 additions & 0 deletions drivers/gpu/drm/arc/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
arcpgu-y := arcpgu_crtc.o arcpgu_hdmi.o arcpgu_drv.o
obj-$(CONFIG_DRM_ARCPGU) += arcpgu.o
50 changes: 50 additions & 0 deletions drivers/gpu/drm/arc/arcpgu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* ARC PGU DRM driver.
*
* Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/

#ifndef _ARCPGU_H_
#define _ARCPGU_H_

struct arcpgu_drm_private {
void __iomem *regs;
struct clk *clk;
struct drm_fbdev_cma *fbdev;
struct drm_framebuffer *fb;
struct list_head event_list;
struct drm_crtc crtc;
struct drm_plane *plane;
};

#define crtc_to_arcpgu_priv(x) container_of(x, struct arcpgu_drm_private, crtc)

static inline void arc_pgu_write(struct arcpgu_drm_private *arcpgu,
unsigned int reg, u32 value)
{
iowrite32(value, arcpgu->regs + reg);
}

static inline u32 arc_pgu_read(struct arcpgu_drm_private *arcpgu,
unsigned int reg)
{
return ioread32(arcpgu->regs + reg);
}

int arc_pgu_setup_crtc(struct drm_device *dev);
int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np);
struct drm_fbdev_cma *arcpgu_fbdev_cma_init(struct drm_device *dev,
unsigned int preferred_bpp, unsigned int num_crtc,
unsigned int max_conn_count);

#endif
257 changes: 257 additions & 0 deletions drivers/gpu/drm/arc/arcpgu_crtc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/*
* ARC PGU DRM driver.
*
* Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/

#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_plane_helper.h>
#include <linux/clk.h>
#include <linux/platform_data/simplefb.h>

#include "arcpgu.h"
#include "arcpgu_regs.h"

#define ENCODE_PGU_XY(x, y) ((((x) - 1) << 16) | ((y) - 1))

static struct simplefb_format supported_formats[] = {
{ "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0}, DRM_FORMAT_RGB565 },
{ "r8g8b8", 24, {16, 8}, {8, 8}, {0, 8}, {0, 0}, DRM_FORMAT_RGB888 },
};

static void arc_pgu_set_pxl_fmt(struct drm_crtc *crtc)
{
struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
uint32_t pixel_format = crtc->primary->state->fb->pixel_format;
struct simplefb_format *format = NULL;
int i;

for (i = 0; i < ARRAY_SIZE(supported_formats); i++) {
if (supported_formats[i].fourcc == pixel_format)
format = &supported_formats[i];
}

if (WARN_ON(!format))
return;

if (format->fourcc == DRM_FORMAT_RGB888)
arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) |
ARCPGU_MODE_RGB888_MASK);

}

static const struct drm_crtc_funcs arc_pgu_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
};

static void arc_pgu_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
struct drm_display_mode *m = &crtc->state->adjusted_mode;
u32 val;

arc_pgu_write(arcpgu, ARCPGU_REG_FMT,
ENCODE_PGU_XY(m->crtc_htotal, m->crtc_vtotal));

arc_pgu_write(arcpgu, ARCPGU_REG_HSYNC,
ENCODE_PGU_XY(m->crtc_hsync_start - m->crtc_hdisplay,
m->crtc_hsync_end - m->crtc_hdisplay));

arc_pgu_write(arcpgu, ARCPGU_REG_VSYNC,
ENCODE_PGU_XY(m->crtc_vsync_start - m->crtc_vdisplay,
m->crtc_vsync_end - m->crtc_vdisplay));

arc_pgu_write(arcpgu, ARCPGU_REG_ACTIVE,
ENCODE_PGU_XY(m->crtc_hblank_end - m->crtc_hblank_start,
m->crtc_vblank_end - m->crtc_vblank_start));

val = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL);

if (m->flags & DRM_MODE_FLAG_PVSYNC)
val |= ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST;
else
val &= ~(ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST);

if (m->flags & DRM_MODE_FLAG_PHSYNC)
val |= ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST;
else
val &= ~(ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST);

arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, val);
arc_pgu_write(arcpgu, ARCPGU_REG_STRIDE, 0);
arc_pgu_write(arcpgu, ARCPGU_REG_START_SET, 1);

arc_pgu_set_pxl_fmt(crtc);

clk_set_rate(arcpgu->clk, m->crtc_clock * 1000);
}

static void arc_pgu_crtc_enable(struct drm_crtc *crtc)
{
struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);

clk_prepare_enable(arcpgu->clk);
arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) |
ARCPGU_CTRL_ENABLE_MASK);
}

static void arc_pgu_crtc_disable(struct drm_crtc *crtc)
{
struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);

if (!crtc->primary->fb)
return;

clk_disable_unprepare(arcpgu->clk);
arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) &
~ARCPGU_CTRL_ENABLE_MASK);
}

static int arc_pgu_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
struct drm_display_mode *mode = &state->adjusted_mode;
long rate, clk_rate = mode->clock * 1000;

rate = clk_round_rate(arcpgu->clk, clk_rate);
if (rate != clk_rate)
return -EINVAL;

return 0;
}

static void arc_pgu_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
unsigned long flags;

if (crtc->state->event) {
struct drm_pending_vblank_event *event = crtc->state->event;

crtc->state->event = NULL;
event->pipe = drm_crtc_index(crtc);

WARN_ON(drm_crtc_vblank_get(crtc) != 0);

spin_lock_irqsave(&crtc->dev->event_lock, flags);
list_add_tail(&event->base.link, &arcpgu->event_list);
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
}
}

static const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs = {
.mode_set = drm_helper_crtc_mode_set,
.mode_set_base = drm_helper_crtc_mode_set_base,
.mode_set_nofb = arc_pgu_crtc_mode_set_nofb,
.enable = arc_pgu_crtc_enable,
.disable = arc_pgu_crtc_disable,
.prepare = arc_pgu_crtc_disable,
.commit = arc_pgu_crtc_enable,
.atomic_check = arc_pgu_crtc_atomic_check,
.atomic_begin = arc_pgu_crtc_atomic_begin,
};

static void arc_pgu_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct arcpgu_drm_private *arcpgu;
struct drm_gem_cma_object *gem;

if (!plane->state->crtc || !plane->state->fb)
return;

arcpgu = crtc_to_arcpgu_priv(plane->state->crtc);
gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0);
arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->paddr);
}

static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = {
.prepare_fb = NULL,
.cleanup_fb = NULL,
.atomic_update = arc_pgu_plane_atomic_update,
};

static void arc_pgu_plane_destroy(struct drm_plane *plane)
{
drm_plane_helper_disable(plane);
drm_plane_cleanup(plane);
}

static const struct drm_plane_funcs arc_pgu_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = arc_pgu_plane_destroy,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};

static struct drm_plane *arc_pgu_plane_init(struct drm_device *drm)
{
struct arcpgu_drm_private *arcpgu = drm->dev_private;
struct drm_plane *plane = NULL;
u32 formats[ARRAY_SIZE(supported_formats)], i;
int ret;

plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL);
if (!plane)
return ERR_PTR(-ENOMEM);

for (i = 0; i < ARRAY_SIZE(supported_formats); i++)
formats[i] = supported_formats[i].fourcc;

ret = drm_universal_plane_init(drm, plane, 0xff, &arc_pgu_plane_funcs,
formats, ARRAY_SIZE(formats),
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret)
return ERR_PTR(ret);

drm_plane_helper_add(plane, &arc_pgu_plane_helper_funcs);
arcpgu->plane = plane;

return plane;
}

int arc_pgu_setup_crtc(struct drm_device *drm)
{
struct arcpgu_drm_private *arcpgu = drm->dev_private;
struct drm_plane *primary;
int ret;

primary = arc_pgu_plane_init(drm);
if (IS_ERR(primary))
return PTR_ERR(primary);

ret = drm_crtc_init_with_planes(drm, &arcpgu->crtc, primary, NULL,
&arc_pgu_crtc_funcs, NULL);
if (ret) {
arc_pgu_plane_destroy(primary);
return ret;
}

drm_crtc_helper_add(&arcpgu->crtc, &arc_pgu_crtc_helper_funcs);
return 0;
}
Loading

0 comments on commit 51dacf2

Please sign in to comment.