Commit 008095e0 authored by Boris Brezillon's avatar Boris Brezillon Committed by Boris Brezillon

drm/vc4: Add support for the transposer block

The transposer block is providing support for mem-to-mem composition,
which is exposed as a drm_writeback connector in DRM.

Add a driver to support this feature.
Signed-off-by: default avatarBoris Brezillon <boris.brezillon@free-electrons.com>
Reviewed-by: default avatarEric Anholt <eric@anholt.net>
Link: https://patchwork.freedesktop.org/patch/msgid/20180703075022.15138-9-boris.brezillon@bootlin.com
parent 1ebe99a7
......@@ -74,6 +74,12 @@ Required properties for DSI:
The 3 clocks output from the DSI analog PHY: dsi[01]_byte,
dsi[01]_ddr2, and dsi[01]_ddr
Required properties for the TXP (writeback) block:
- compatible: Should be "brcm,bcm2835-txp"
- reg: Physical base address and length of the TXP block's registers
- interrupts: The interrupt number
See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
[1] Documentation/devicetree/bindings/media/video-interfaces.txt
Example:
......
......@@ -19,6 +19,7 @@ vc4-y := \
vc4_plane.o \
vc4_render_cl.o \
vc4_trace_points.o \
vc4_txp.o \
vc4_v3d.o \
vc4_validate.o \
vc4_validate_shaders.o
......
......@@ -46,6 +46,8 @@ struct vc4_crtc_state {
struct drm_crtc_state base;
/* Dlist area for this CRTC configuration. */
struct drm_mm_node mm;
bool feed_txp;
bool txp_armed;
};
static inline struct vc4_crtc_state *
......@@ -324,10 +326,8 @@ static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc)
return NULL;
}
static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
static void vc4_crtc_config_pv(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc);
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
......@@ -338,12 +338,6 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 ||
vc4_encoder->type == VC4_ENCODER_TYPE_DSI1);
u32 format = is_dsi ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24;
bool debug_dump_regs = false;
if (debug_dump_regs) {
DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc));
vc4_crtc_dump_regs(vc4_crtc);
}
/* Reset the PV fifo. */
CRTC_WRITE(PV_CONTROL, 0);
......@@ -419,6 +413,49 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
PV_CONTROL_CLK_SELECT) |
PV_CONTROL_FIFO_CLR |
PV_CONTROL_EN);
}
static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
bool debug_dump_regs = false;
if (debug_dump_regs) {
DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc));
vc4_crtc_dump_regs(vc4_crtc);
}
if (vc4_crtc->channel == 2) {
u32 dispctrl;
u32 dsp3_mux;
/*
* SCALER_DISPCTRL_DSP3 = X, where X < 2 means 'connect DSP3 to
* FIFO X'.
* SCALER_DISPCTRL_DSP3 = 3 means 'disable DSP 3'.
*
* DSP3 is connected to FIFO2 unless the transposer is
* enabled. In this case, FIFO 2 is directly accessed by the
* TXP IP, and we need to disable the FIFO2 -> pixelvalve1
* route.
*/
if (vc4_state->feed_txp)
dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX);
else
dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX);
dispctrl = HVS_READ(SCALER_DISPCTRL) &
~SCALER_DISPCTRL_DSP3_MUX_MASK;
HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux);
}
if (!vc4_state->feed_txp)
vc4_crtc_config_pv(crtc);
HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel),
SCALER_DISPBKGND_AUTOHS |
......@@ -499,6 +536,13 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc,
}
}
void vc4_crtc_txp_armed(struct drm_crtc_state *state)
{
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
vc4_state->txp_armed = true;
}
static void vc4_crtc_update_dlist(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
......@@ -514,8 +558,11 @@ static void vc4_crtc_update_dlist(struct drm_crtc *crtc)
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
spin_lock_irqsave(&dev->event_lock, flags);
vc4_crtc->event = crtc->state->event;
crtc->state->event = NULL;
if (!vc4_state->feed_txp || vc4_state->txp_armed) {
vc4_crtc->event = crtc->state->event;
crtc->state->event = NULL;
}
HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
vc4_state->mm.start);
......@@ -533,8 +580,8 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
struct drm_crtc_state *state = crtc->state;
struct drm_display_mode *mode = &state->adjusted_mode;
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
require_hvs_enabled(dev);
......@@ -546,15 +593,21 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
/* Turn on the scaler, which will wait for vstart to start
* compositing.
* When feeding the transposer, we should operate in oneshot
* mode.
*/
HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel),
VC4_SET_FIELD(mode->hdisplay, SCALER_DISPCTRLX_WIDTH) |
VC4_SET_FIELD(mode->vdisplay, SCALER_DISPCTRLX_HEIGHT) |
SCALER_DISPCTRLX_ENABLE);
SCALER_DISPCTRLX_ENABLE |
(vc4_state->feed_txp ? SCALER_DISPCTRLX_ONESHOT : 0));
/* Turn on the pixel valve, which will emit the vstart signal. */
CRTC_WRITE(PV_V_CONTROL,
CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
/* When feeding the transposer block the pixelvalve is unneeded and
* should not be enabled.
*/
if (!vc4_state->feed_txp)
CRTC_WRITE(PV_V_CONTROL,
CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
}
static enum drm_mode_status vc4_crtc_mode_valid(struct drm_crtc *crtc,
......@@ -579,8 +632,10 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_plane *plane;
unsigned long flags;
const struct drm_plane_state *plane_state;
struct drm_connector *conn;
struct drm_connector_state *conn_state;
u32 dlist_count = 0;
int ret;
int ret, i;
/* The pixelvalve can only feed one encoder (and encoders are
* 1:1 with connectors.)
......@@ -600,6 +655,24 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
if (ret)
return ret;
for_each_new_connector_in_state(state->state, conn, conn_state, i) {
if (conn_state->crtc != crtc)
continue;
/* The writeback connector is implemented using the transposer
* block which is directly taking its data from the HVS FIFO.
*/
if (conn->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) {
state->no_vblank = true;
vc4_state->feed_txp = true;
} else {
state->no_vblank = false;
vc4_state->feed_txp = false;
}
break;
}
return 0;
}
......@@ -713,7 +786,8 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
spin_lock_irqsave(&dev->event_lock, flags);
if (vc4_crtc->event &&
(vc4_state->mm.start == HVS_READ(SCALER_DISPLACTX(chan)))) {
(vc4_state->mm.start == HVS_READ(SCALER_DISPLACTX(chan)) ||
vc4_state->feed_txp)) {
drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
vc4_crtc->event = NULL;
drm_crtc_vblank_put(crtc);
......@@ -721,6 +795,13 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
spin_unlock_irqrestore(&dev->event_lock, flags);
}
void vc4_crtc_handle_vblank(struct vc4_crtc *crtc)
{
crtc->t_vblank = ktime_get();
drm_crtc_handle_vblank(&crtc->base);
vc4_crtc_handle_page_flip(crtc);
}
static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
{
struct vc4_crtc *vc4_crtc = data;
......@@ -728,10 +809,8 @@ 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);
vc4_crtc_handle_vblank(vc4_crtc);
ret = IRQ_HANDLED;
}
......@@ -884,12 +963,15 @@ static int vc4_page_flip(struct drm_crtc *crtc,
static struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc)
{
struct vc4_crtc_state *vc4_state;
struct vc4_crtc_state *vc4_state, *old_vc4_state;
vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
if (!vc4_state)
return NULL;
old_vc4_state = to_vc4_crtc_state(crtc->state);
vc4_state->feed_txp = old_vc4_state->feed_txp;
__drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
return &vc4_state->base;
}
......@@ -987,9 +1069,17 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm,
struct drm_encoder *encoder;
drm_for_each_encoder(encoder, drm) {
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
struct vc4_encoder *vc4_encoder;
int i;
/* HVS FIFO2 can feed the TXP IP. */
if (crtc_data->hvs_channel == 2 &&
encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) {
encoder->possible_crtcs |= drm_crtc_mask(crtc);
continue;
}
vc4_encoder = to_vc4_encoder(encoder);
for (i = 0; i < ARRAY_SIZE(crtc_data->encoder_types); i++) {
if (vc4_encoder->type == encoder_types[i]) {
vc4_encoder->clock_select = i;
......
......@@ -21,6 +21,7 @@ static const struct drm_info_list vc4_debugfs_list[] = {
{"dsi1_regs", vc4_dsi_debugfs_regs, 0, (void *)(uintptr_t)1},
{"hdmi_regs", vc4_hdmi_debugfs_regs, 0},
{"vec_regs", vc4_vec_debugfs_regs, 0},
{"txp_regs", vc4_txp_debugfs_regs, 0},
{"hvs_regs", vc4_hvs_debugfs_regs, 0},
{"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0},
{"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1},
......
......@@ -344,6 +344,7 @@ static struct platform_driver *const component_drivers[] = {
&vc4_vec_driver,
&vc4_dpi_driver,
&vc4_dsi_driver,
&vc4_txp_driver,
&vc4_hvs_driver,
&vc4_crtc_driver,
&vc4_v3d_driver,
......
......@@ -73,6 +73,7 @@ struct vc4_dev {
struct vc4_dpi *dpi;
struct vc4_dsi *dsi1;
struct vc4_vec *vec;
struct vc4_txp *txp;
struct vc4_hang_state *hang_state;
......@@ -698,6 +699,8 @@ bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
bool in_vblank_irq, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime,
const struct drm_display_mode *mode);
void vc4_crtc_handle_vblank(struct vc4_crtc *crtc);
void vc4_crtc_txp_armed(struct drm_crtc_state *state);
/* vc4_debugfs.c */
int vc4_debugfs_init(struct drm_minor *minor);
......@@ -745,6 +748,10 @@ int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused);
extern struct platform_driver vc4_vec_driver;
int vc4_vec_debugfs_regs(struct seq_file *m, void *unused);
/* vc4_txp.c */
extern struct platform_driver vc4_txp_driver;
int vc4_txp_debugfs_regs(struct seq_file *m, void *unused);
/* vc4_irq.c */
irqreturn_t vc4_irq(int irq, void *arg);
void vc4_irq_preinstall(struct drm_device *dev);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment