mirror of
https://github.com/torvalds/linux.git
synced 2024-11-15 16:41:58 +00:00
This pull request brings in new vc4 plane formats for Android, precise
vblank timestamping, and a couple of small cleanups. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCgAGBQJXhUULAAoJELXWKTbR/J7oZ1oQAKnTTJlnCbaSNrWbRBUuaMtO S2RQKyMI2LOpf4XE13cNHm0IaYNOw6hJIXlnxeuzHbQSGvOrHnabluZuvAfLa3OQ 4bb8K0elWsPRbtmx2L8DjncLCmmmOsbczrTwo3efq70XZs4PyaCp2vHpCU8kI7HJ xO/fk6E8sYDPFCxZxb82C7LTSjQBlPn58dD+cEFg1kGILOhyR1eZwRj8e4netjKU gN/059R6VPPor+I8oIMrqoHAxAlZUGnVPsK/1rgR5cfxlC1NPKU71eI7xiBLNclt 86BcvU/LuwAYt81UirnGTQU/m47dCMK4mmpzD/9EgVW/KTUPLPubML+u9fL67+B4 BJ7T7N4t7APblqkO/iI9DcTAkZNmydq4sdsfDdXcVZ3umDTNpmb/PQuq+rFEc1CZ qsBw13kJ4bHBYUpZvrxuCesSRLpXhUiSOg/uVnRHTdiALZqQQyzQlnw6Q4GCMKiz R99/k4/7/cAgQkGElwLeHR8Aw0r26m+X4pnLN9lrafjQjlH04CcuvFwlak28jnit Oi0eZ0VD9RTHwPzUIoe4M32Q/7oAe7y5b7gCNPbM/0s56WQv+cIHi7CdgdIYtUWs BIyeBTzpLvzkEvrEMYoipgpv3scbudMssTd3bo8gsdOu0tllBzi59Poo5hl6pQbs lseJ7LavmK5sPDPsIZb3 =aF3C -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
35b8a74924
@ -46,12 +46,17 @@ struct vc4_crtc {
|
||||
const struct vc4_crtc_data *data;
|
||||
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. */
|
||||
int channel;
|
||||
|
||||
u8 lut_r[256];
|
||||
u8 lut_g[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;
|
||||
};
|
||||
@ -146,6 +151,144 @@ int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused)
|
||||
}
|
||||
#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)
|
||||
{
|
||||
drm_crtc_cleanup(crtc);
|
||||
@ -519,6 +662,7 @@ static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
if (stat & PV_INT_VFP_START) {
|
||||
vc4_crtc->t_vblank = ktime_get();
|
||||
CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
|
||||
drm_crtc_handle_vblank(&vc4_crtc->base);
|
||||
vc4_crtc_handle_page_flip(vc4_crtc);
|
||||
@ -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)
|
||||
{
|
||||
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)
|
||||
crtc->cursor = cursor_plane;
|
||||
}
|
||||
|
||||
vc4_crtc_get_cob_allocation(vc4_crtc);
|
||||
|
||||
CRTC_WRITE(PV_INTEN, 0);
|
||||
CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
|
||||
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,
|
||||
{
|
||||
struct drm_connector *connector = NULL;
|
||||
struct vc4_dpi_connector *dpi_connector;
|
||||
int ret = 0;
|
||||
|
||||
dpi_connector = devm_kzalloc(dev->dev, sizeof(*dpi_connector),
|
||||
GFP_KERNEL);
|
||||
if (!dpi_connector) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
if (!dpi_connector)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
connector = &dpi_connector->base;
|
||||
|
||||
dpi_connector->encoder = dpi->encoder;
|
||||
@ -251,12 +249,6 @@ static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev,
|
||||
drm_mode_connector_attach_encoder(connector, dpi->encoder);
|
||||
|
||||
return connector;
|
||||
|
||||
fail:
|
||||
if (connector)
|
||||
vc4_dpi_connector_destroy(connector);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = {
|
||||
|
@ -92,6 +92,8 @@ static struct drm_driver vc4_drm_driver = {
|
||||
.enable_vblank = vc4_enable_vblank,
|
||||
.disable_vblank = vc4_disable_vblank,
|
||||
.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)
|
||||
.debugfs_init = vc4_debugfs_init,
|
||||
@ -195,8 +197,6 @@ static int vc4_drm_bind(struct device *dev)
|
||||
vc4_bo_cache_init(drm);
|
||||
|
||||
drm_mode_config_init(drm);
|
||||
if (ret)
|
||||
goto unref;
|
||||
|
||||
vc4_gem_init(drm);
|
||||
|
||||
@ -218,7 +218,6 @@ unbind_all:
|
||||
component_unbind_all(dev, drm);
|
||||
gem_destroy:
|
||||
vc4_gem_destroy(drm);
|
||||
unref:
|
||||
drm_dev_unref(drm);
|
||||
vc4_bo_cache_destroy(drm);
|
||||
return ret;
|
||||
@ -246,8 +245,8 @@ static const struct component_master_ops vc4_drm_ops = {
|
||||
static struct platform_driver *const component_drivers[] = {
|
||||
&vc4_hdmi_driver,
|
||||
&vc4_dpi_driver,
|
||||
&vc4_crtc_driver,
|
||||
&vc4_hvs_driver,
|
||||
&vc4_crtc_driver,
|
||||
&vc4_v3d_driver,
|
||||
};
|
||||
|
||||
|
@ -415,6 +415,13 @@ extern struct platform_driver vc4_crtc_driver;
|
||||
int vc4_enable_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_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 */
|
||||
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)
|
||||
if (IS_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");
|
||||
if (IS_ERR(hdmi->pixel_clock)) {
|
||||
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)
|
||||
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);
|
||||
of_node_put(ddc_node);
|
||||
if (!hdmi->ddc) {
|
||||
DRM_DEBUG("Failed to get ddc i2c adapter by node\n");
|
||||
return -EPROBE_DEFER;
|
||||
|
@ -93,6 +93,14 @@ static const struct hvs_format {
|
||||
.drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
|
||||
.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,
|
||||
.pixel_order = HVS_PIXEL_ORDER_XRGB, .has_alpha = false,
|
||||
|
@ -366,7 +366,6 @@
|
||||
# define SCALER_DISPBKGND_FILL BIT(24)
|
||||
|
||||
#define SCALER_DISPSTAT0 0x00000048
|
||||
#define SCALER_DISPBASE0 0x0000004c
|
||||
# define SCALER_DISPSTATX_MODE_MASK VC4_MASK(31, 30)
|
||||
# define SCALER_DISPSTATX_MODE_SHIFT 30
|
||||
# define SCALER_DISPSTATX_MODE_DISABLED 0
|
||||
@ -375,6 +374,24 @@
|
||||
# define SCALER_DISPSTATX_MODE_EOF 3
|
||||
# define SCALER_DISPSTATX_FULL BIT(29)
|
||||
# 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_DISPBKGND1 0x00000054
|
||||
#define SCALER_DISPBKGNDX(x) (SCALER_DISPBKGND0 + \
|
||||
@ -385,6 +402,9 @@
|
||||
(x) * (SCALER_DISPSTAT1 - \
|
||||
SCALER_DISPSTAT0))
|
||||
#define SCALER_DISPBASE1 0x0000005c
|
||||
#define SCALER_DISPBASEX(x) (SCALER_DISPBASE0 + \
|
||||
(x) * (SCALER_DISPBASE1 - \
|
||||
SCALER_DISPBASE0))
|
||||
#define SCALER_DISPCTRL2 0x00000060
|
||||
#define SCALER_DISPCTRLX(x) (SCALER_DISPCTRL0 + \
|
||||
(x) * (SCALER_DISPCTRL1 - \
|
||||
|
Loading…
Reference in New Issue
Block a user