-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drm: Add support of ARC PGU display controller
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
Showing
9 changed files
with
845 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.