提交 35b8a749 编写于 作者: D Dave Airlie

Merge tag 'drm-vc4-next-2016-07-12' of https://github.com/anholt/linux into drm-next

This pull request brings in new vc4 plane formats for Android, precise
vblank timestamping, and a couple of small cleanups.

* tag 'drm-vc4-next-2016-07-12' of https://github.com/anholt/linux:
  drm/vc4: remove redundant ret status check
  drm/vc4: Implement precise vblank timestamping.
  drm/vc4: Bind the HVS before we bind the individual CRTCs.
  gpu: drm: vc4_hdmi: add missing of_node_put after calling of_parse_phandle
  drm: vc4: enable XBGR8888 and ABGR8888 pixel formats
  drm/vc4: clean up error exit path on failed dpi_connector allocation
...@@ -46,12 +46,17 @@ struct vc4_crtc { ...@@ -46,12 +46,17 @@ struct vc4_crtc {
const struct vc4_crtc_data *data; const struct vc4_crtc_data *data;
void __iomem *regs; void __iomem *regs;
/* Timestamp at start of vblank irq - unaffected by lock delays. */
ktime_t t_vblank;
/* Which HVS channel we're using for our CRTC. */ /* Which HVS channel we're using for our CRTC. */
int channel; int channel;
u8 lut_r[256]; u8 lut_r[256];
u8 lut_g[256]; u8 lut_g[256];
u8 lut_b[256]; u8 lut_b[256];
/* Size in pixels of the COB memory allocated to this CRTC. */
u32 cob_size;
struct drm_pending_vblank_event *event; struct drm_pending_vblank_event *event;
}; };
...@@ -146,6 +151,144 @@ int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused) ...@@ -146,6 +151,144 @@ int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused)
} }
#endif #endif
int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
unsigned int flags, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime,
const struct drm_display_mode *mode)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
u32 val;
int fifo_lines;
int vblank_lines;
int ret = 0;
/*
* XXX Doesn't work well in interlaced mode yet, partially due
* to problems in vc4 kms or drm core interlaced mode handling,
* so disable for now in interlaced mode.
*/
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
return ret;
/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
/* Get optional system timestamp before query. */
if (stime)
*stime = ktime_get();
/*
* Read vertical scanline which is currently composed for our
* pixelvalve by the HVS, and also the scaler status.
*/
val = HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel));
/* Get optional system timestamp after query. */
if (etime)
*etime = ktime_get();
/* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
/* Vertical position of hvs composed scanline. */
*vpos = VC4_GET_FIELD(val, SCALER_DISPSTATX_LINE);
/* No hpos info available. */
if (hpos)
*hpos = 0;
/* This is the offset we need for translating hvs -> pv scanout pos. */
fifo_lines = vc4_crtc->cob_size / mode->crtc_hdisplay;
if (fifo_lines > 0)
ret |= DRM_SCANOUTPOS_VALID;
/* HVS more than fifo_lines into frame for compositing? */
if (*vpos > fifo_lines) {
/*
* We are in active scanout and can get some meaningful results
* from HVS. The actual PV scanout can not trail behind more
* than fifo_lines as that is the fifo's capacity. Assume that
* in active scanout the HVS and PV work in lockstep wrt. HVS
* refilling the fifo and PV consuming from the fifo, ie.
* whenever the PV consumes and frees up a scanline in the
* fifo, the HVS will immediately refill it, therefore
* incrementing vpos. Therefore we choose HVS read position -
* fifo size in scanlines as a estimate of the real scanout
* position of the PV.
*/
*vpos -= fifo_lines + 1;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
*vpos /= 2;
ret |= DRM_SCANOUTPOS_ACCURATE;
return ret;
}
/*
* Less: This happens when we are in vblank and the HVS, after getting
* the VSTART restart signal from the PV, just started refilling its
* fifo with new lines from the top-most lines of the new framebuffers.
* The PV does not scan out in vblank, so does not remove lines from
* the fifo, so the fifo will be full quickly and the HVS has to pause.
* We can't get meaningful readings wrt. scanline position of the PV
* and need to make things up in a approximative but consistent way.
*/
ret |= DRM_SCANOUTPOS_IN_VBLANK;
vblank_lines = mode->crtc_vtotal - mode->crtc_vdisplay;
if (flags & DRM_CALLED_FROM_VBLIRQ) {
/*
* Assume the irq handler got called close to first
* line of vblank, so PV has about a full vblank
* scanlines to go, and as a base timestamp use the
* one taken at entry into vblank irq handler, so it
* is not affected by random delays due to lock
* contention on event_lock or vblank_time lock in
* the core.
*/
*vpos = -vblank_lines;
if (stime)
*stime = vc4_crtc->t_vblank;
if (etime)
*etime = vc4_crtc->t_vblank;
/*
* If the HVS fifo is not yet full then we know for certain
* we are at the very beginning of vblank, as the hvs just
* started refilling, and the stime and etime timestamps
* truly correspond to start of vblank.
*/
if ((val & SCALER_DISPSTATX_FULL) != SCALER_DISPSTATX_FULL)
ret |= DRM_SCANOUTPOS_ACCURATE;
} else {
/*
* No clue where we are inside vblank. Return a vpos of zero,
* which will cause calling code to just return the etime
* timestamp uncorrected. At least this is no worse than the
* standard fallback.
*/
*vpos = 0;
}
return ret;
}
int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id,
int *max_error, struct timeval *vblank_time,
unsigned flags)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
struct drm_crtc *crtc = &vc4_crtc->base;
struct drm_crtc_state *state = crtc->state;
/* Helper routine in DRM core does all the work: */
return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc_id, max_error,
vblank_time, flags,
&state->adjusted_mode);
}
static void vc4_crtc_destroy(struct drm_crtc *crtc) static void vc4_crtc_destroy(struct drm_crtc *crtc)
{ {
drm_crtc_cleanup(crtc); drm_crtc_cleanup(crtc);
...@@ -519,6 +662,7 @@ static irqreturn_t vc4_crtc_irq_handler(int irq, void *data) ...@@ -519,6 +662,7 @@ static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
if (stat & PV_INT_VFP_START) { if (stat & PV_INT_VFP_START) {
vc4_crtc->t_vblank = ktime_get();
CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START); CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
drm_crtc_handle_vblank(&vc4_crtc->base); drm_crtc_handle_vblank(&vc4_crtc->base);
vc4_crtc_handle_page_flip(vc4_crtc); vc4_crtc_handle_page_flip(vc4_crtc);
...@@ -723,6 +867,22 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm, ...@@ -723,6 +867,22 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm,
} }
} }
static void
vc4_crtc_get_cob_allocation(struct vc4_crtc *vc4_crtc)
{
struct drm_device *drm = vc4_crtc->base.dev;
struct vc4_dev *vc4 = to_vc4_dev(drm);
u32 dispbase = HVS_READ(SCALER_DISPBASEX(vc4_crtc->channel));
/* Top/base are supposed to be 4-pixel aligned, but the
* Raspberry Pi firmware fills the low bits (which are
* presumably ignored).
*/
u32 top = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_TOP) & ~3;
u32 base = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_BASE) & ~3;
vc4_crtc->cob_size = top - base + 4;
}
static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
{ {
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
...@@ -799,6 +959,8 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) ...@@ -799,6 +959,8 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
crtc->cursor = cursor_plane; crtc->cursor = cursor_plane;
} }
vc4_crtc_get_cob_allocation(vc4_crtc);
CRTC_WRITE(PV_INTEN, 0); CRTC_WRITE(PV_INTEN, 0);
CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START); CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
ret = devm_request_irq(dev, platform_get_irq(pdev, 0), ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
......
...@@ -227,14 +227,12 @@ static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev, ...@@ -227,14 +227,12 @@ static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev,
{ {
struct drm_connector *connector = NULL; struct drm_connector *connector = NULL;
struct vc4_dpi_connector *dpi_connector; struct vc4_dpi_connector *dpi_connector;
int ret = 0;
dpi_connector = devm_kzalloc(dev->dev, sizeof(*dpi_connector), dpi_connector = devm_kzalloc(dev->dev, sizeof(*dpi_connector),
GFP_KERNEL); GFP_KERNEL);
if (!dpi_connector) { if (!dpi_connector)
ret = -ENOMEM; return ERR_PTR(-ENOMEM);
goto fail;
}
connector = &dpi_connector->base; connector = &dpi_connector->base;
dpi_connector->encoder = dpi->encoder; dpi_connector->encoder = dpi->encoder;
...@@ -251,12 +249,6 @@ static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev, ...@@ -251,12 +249,6 @@ static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev,
drm_mode_connector_attach_encoder(connector, dpi->encoder); drm_mode_connector_attach_encoder(connector, dpi->encoder);
return connector; return connector;
fail:
if (connector)
vc4_dpi_connector_destroy(connector);
return ERR_PTR(ret);
} }
static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = { static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = {
......
...@@ -92,6 +92,8 @@ static struct drm_driver vc4_drm_driver = { ...@@ -92,6 +92,8 @@ static struct drm_driver vc4_drm_driver = {
.enable_vblank = vc4_enable_vblank, .enable_vblank = vc4_enable_vblank,
.disable_vblank = vc4_disable_vblank, .disable_vblank = vc4_disable_vblank,
.get_vblank_counter = drm_vblank_no_hw_counter, .get_vblank_counter = drm_vblank_no_hw_counter,
.get_scanout_position = vc4_crtc_get_scanoutpos,
.get_vblank_timestamp = vc4_crtc_get_vblank_timestamp,
#if defined(CONFIG_DEBUG_FS) #if defined(CONFIG_DEBUG_FS)
.debugfs_init = vc4_debugfs_init, .debugfs_init = vc4_debugfs_init,
...@@ -195,8 +197,6 @@ static int vc4_drm_bind(struct device *dev) ...@@ -195,8 +197,6 @@ static int vc4_drm_bind(struct device *dev)
vc4_bo_cache_init(drm); vc4_bo_cache_init(drm);
drm_mode_config_init(drm); drm_mode_config_init(drm);
if (ret)
goto unref;
vc4_gem_init(drm); vc4_gem_init(drm);
...@@ -218,7 +218,6 @@ static int vc4_drm_bind(struct device *dev) ...@@ -218,7 +218,6 @@ static int vc4_drm_bind(struct device *dev)
component_unbind_all(dev, drm); component_unbind_all(dev, drm);
gem_destroy: gem_destroy:
vc4_gem_destroy(drm); vc4_gem_destroy(drm);
unref:
drm_dev_unref(drm); drm_dev_unref(drm);
vc4_bo_cache_destroy(drm); vc4_bo_cache_destroy(drm);
return ret; return ret;
...@@ -246,8 +245,8 @@ static const struct component_master_ops vc4_drm_ops = { ...@@ -246,8 +245,8 @@ static const struct component_master_ops vc4_drm_ops = {
static struct platform_driver *const component_drivers[] = { static struct platform_driver *const component_drivers[] = {
&vc4_hdmi_driver, &vc4_hdmi_driver,
&vc4_dpi_driver, &vc4_dpi_driver,
&vc4_crtc_driver,
&vc4_hvs_driver, &vc4_hvs_driver,
&vc4_crtc_driver,
&vc4_v3d_driver, &vc4_v3d_driver,
}; };
......
...@@ -415,6 +415,13 @@ extern struct platform_driver vc4_crtc_driver; ...@@ -415,6 +415,13 @@ extern struct platform_driver vc4_crtc_driver;
int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id); int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id);
void vc4_disable_vblank(struct drm_device *dev, unsigned int crtc_id); void vc4_disable_vblank(struct drm_device *dev, unsigned int crtc_id);
int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg); int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg);
int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
unsigned int flags, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime,
const struct drm_display_mode *mode);
int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id,
int *max_error, struct timeval *vblank_time,
unsigned flags);
/* vc4_debugfs.c */ /* vc4_debugfs.c */
int vc4_debugfs_init(struct drm_minor *minor); int vc4_debugfs_init(struct drm_minor *minor);
......
...@@ -456,12 +456,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) ...@@ -456,12 +456,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
if (IS_ERR(hdmi->hd_regs)) if (IS_ERR(hdmi->hd_regs))
return PTR_ERR(hdmi->hd_regs); return PTR_ERR(hdmi->hd_regs);
ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
if (!ddc_node) {
DRM_ERROR("Failed to find ddc node in device tree\n");
return -ENODEV;
}
hdmi->pixel_clock = devm_clk_get(dev, "pixel"); hdmi->pixel_clock = devm_clk_get(dev, "pixel");
if (IS_ERR(hdmi->pixel_clock)) { if (IS_ERR(hdmi->pixel_clock)) {
DRM_ERROR("Failed to get pixel clock\n"); DRM_ERROR("Failed to get pixel clock\n");
...@@ -473,7 +467,14 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) ...@@ -473,7 +467,14 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
return PTR_ERR(hdmi->hsm_clock); return PTR_ERR(hdmi->hsm_clock);
} }
ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
if (!ddc_node) {
DRM_ERROR("Failed to find ddc node in device tree\n");
return -ENODEV;
}
hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node); hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
if (!hdmi->ddc) { if (!hdmi->ddc) {
DRM_DEBUG("Failed to get ddc i2c adapter by node\n"); DRM_DEBUG("Failed to get ddc i2c adapter by node\n");
return -EPROBE_DEFER; return -EPROBE_DEFER;
......
...@@ -93,6 +93,14 @@ static const struct hvs_format { ...@@ -93,6 +93,14 @@ static const struct hvs_format {
.drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true, .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
}, },
{
.drm = DRM_FORMAT_ABGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
.pixel_order = HVS_PIXEL_ORDER_ARGB, .has_alpha = true,
},
{
.drm = DRM_FORMAT_XBGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
.pixel_order = HVS_PIXEL_ORDER_ARGB, .has_alpha = false,
},
{ {
.drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565, .drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565,
.pixel_order = HVS_PIXEL_ORDER_XRGB, .has_alpha = false, .pixel_order = HVS_PIXEL_ORDER_XRGB, .has_alpha = false,
......
...@@ -366,7 +366,6 @@ ...@@ -366,7 +366,6 @@
# define SCALER_DISPBKGND_FILL BIT(24) # define SCALER_DISPBKGND_FILL BIT(24)
#define SCALER_DISPSTAT0 0x00000048 #define SCALER_DISPSTAT0 0x00000048
#define SCALER_DISPBASE0 0x0000004c
# define SCALER_DISPSTATX_MODE_MASK VC4_MASK(31, 30) # define SCALER_DISPSTATX_MODE_MASK VC4_MASK(31, 30)
# define SCALER_DISPSTATX_MODE_SHIFT 30 # define SCALER_DISPSTATX_MODE_SHIFT 30
# define SCALER_DISPSTATX_MODE_DISABLED 0 # define SCALER_DISPSTATX_MODE_DISABLED 0
...@@ -375,6 +374,24 @@ ...@@ -375,6 +374,24 @@
# define SCALER_DISPSTATX_MODE_EOF 3 # define SCALER_DISPSTATX_MODE_EOF 3
# define SCALER_DISPSTATX_FULL BIT(29) # define SCALER_DISPSTATX_FULL BIT(29)
# define SCALER_DISPSTATX_EMPTY BIT(28) # define SCALER_DISPSTATX_EMPTY BIT(28)
# define SCALER_DISPSTATX_FRAME_COUNT_MASK VC4_MASK(17, 12)
# define SCALER_DISPSTATX_FRAME_COUNT_SHIFT 12
# define SCALER_DISPSTATX_LINE_MASK VC4_MASK(11, 0)
# define SCALER_DISPSTATX_LINE_SHIFT 0
#define SCALER_DISPBASE0 0x0000004c
/* Last pixel in the COB (display FIFO memory) allocated to this HVS
* channel. Must be 4-pixel aligned (and thus 4 pixels less than the
* next COB base).
*/
# define SCALER_DISPBASEX_TOP_MASK VC4_MASK(31, 16)
# define SCALER_DISPBASEX_TOP_SHIFT 16
/* First pixel in the COB (display FIFO memory) allocated to this HVS
* channel. Must be 4-pixel aligned.
*/
# define SCALER_DISPBASEX_BASE_MASK VC4_MASK(15, 0)
# define SCALER_DISPBASEX_BASE_SHIFT 0
#define SCALER_DISPCTRL1 0x00000050 #define SCALER_DISPCTRL1 0x00000050
#define SCALER_DISPBKGND1 0x00000054 #define SCALER_DISPBKGND1 0x00000054
#define SCALER_DISPBKGNDX(x) (SCALER_DISPBKGND0 + \ #define SCALER_DISPBKGNDX(x) (SCALER_DISPBKGND0 + \
...@@ -385,6 +402,9 @@ ...@@ -385,6 +402,9 @@
(x) * (SCALER_DISPSTAT1 - \ (x) * (SCALER_DISPSTAT1 - \
SCALER_DISPSTAT0)) SCALER_DISPSTAT0))
#define SCALER_DISPBASE1 0x0000005c #define SCALER_DISPBASE1 0x0000005c
#define SCALER_DISPBASEX(x) (SCALER_DISPBASE0 + \
(x) * (SCALER_DISPBASE1 - \
SCALER_DISPBASE0))
#define SCALER_DISPCTRL2 0x00000060 #define SCALER_DISPCTRL2 0x00000060
#define SCALER_DISPCTRLX(x) (SCALER_DISPCTRL0 + \ #define SCALER_DISPCTRLX(x) (SCALER_DISPCTRL0 + \
(x) * (SCALER_DISPCTRL1 - \ (x) * (SCALER_DISPCTRL1 - \
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册