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;
- }
-
vc4_state->margins.left = conn_state->tv.margins.left;
vc4_state->margins.right = conn_state->tv.margins.right;
vc4_state->margins.top = conn_state->tv.margins.top;
struct drm_crtc *crtc)
{
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- const struct vc4_crtc_data *crtc_data = vc4_crtc_to_vc4_crtc_data(vc4_crtc);
const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc);
const enum vc4_encoder_type *encoder_types = pv_data->encoder_types;
struct drm_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(pv_data->encoder_types); i++) {
if (vc4_encoder->type == encoder_types[i]) {
#include <drm/drm_fourcc.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
#include <drm/drm_writeback.h>
#include "vc4_drv.h"
#define TXP_WRITE(offset, val) writel(val, txp->regs + (offset))
struct vc4_txp {
+ struct vc4_crtc base;
+
struct platform_device *pdev;
struct drm_writeback_connector connector;
.disable = vc4_txp_encoder_disable,
};
+static int vc4_txp_enable_vblank(struct drm_crtc *crtc)
+{
+ return 0;
+}
+
+static void vc4_txp_disable_vblank(struct drm_crtc *crtc) {}
+
+static const struct drm_crtc_funcs vc4_txp_crtc_funcs = {
+ .set_config = drm_atomic_helper_set_config,
+ .destroy = vc4_crtc_destroy,
+ .page_flip = vc4_page_flip,
+ .reset = vc4_crtc_reset,
+ .atomic_duplicate_state = vc4_crtc_duplicate_state,
+ .atomic_destroy_state = vc4_crtc_destroy_state,
+ .gamma_set = drm_atomic_helper_legacy_gamma_set,
+ .enable_vblank = vc4_txp_enable_vblank,
+ .disable_vblank = vc4_txp_disable_vblank,
+};
+
+static int vc4_txp_atomic_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
+ int ret;
+
+ ret = vc4_hvs_atomic_check(crtc, state);
+ if (ret)
+ return ret;
+
+ state->no_vblank = true;
+ vc4_state->feed_txp = true;
+
+ return 0;
+}
+
+static void vc4_txp_atomic_enable(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ drm_crtc_vblank_on(crtc);
+ vc4_hvs_atomic_enable(crtc, old_state);
+}
+
+static void vc4_txp_atomic_disable(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct drm_device *dev = crtc->dev;
+
+ /* Disable vblank irq handling before crtc is disabled. */
+ drm_crtc_vblank_off(crtc);
+
+ vc4_hvs_atomic_disable(crtc, old_state);
+
+ /*
+ * Make sure we issue a vblank event after disabling the CRTC if
+ * someone was waiting it.
+ */
+ if (crtc->state->event) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ crtc->state->event = NULL;
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ }
+}
+
+static const struct drm_crtc_helper_funcs vc4_txp_crtc_helper_funcs = {
+ .atomic_check = vc4_txp_atomic_check,
+ .atomic_flush = vc4_hvs_atomic_flush,
+ .atomic_enable = vc4_txp_atomic_enable,
+ .atomic_disable = vc4_txp_atomic_disable,
+ .mode_set_nofb = vc4_hvs_mode_set_nofb,
+};
+
static irqreturn_t vc4_txp_interrupt(int irq, void *data)
{
struct vc4_txp *txp = data;
+ struct vc4_crtc *vc4_crtc = &txp->base;
TXP_WRITE(TXP_DST_CTRL, TXP_READ(TXP_DST_CTRL) & ~TXP_EI);
- vc4_crtc_handle_vblank(to_vc4_crtc(txp->connector.base.state->crtc));
+ vc4_crtc_handle_vblank(vc4_crtc);
drm_writeback_signal_completion(&txp->connector, 0);
return IRQ_HANDLED;
}
+static const struct vc4_crtc_data vc4_txp_crtc_data = {
+ .hvs_channel = 2,
+};
+
static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm = dev_get_drvdata(master);
struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct vc4_crtc *vc4_crtc;
struct vc4_txp *txp;
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
int ret, irq;
irq = platform_get_irq(pdev, 0);
txp = devm_kzalloc(dev, sizeof(*txp), GFP_KERNEL);
if (!txp)
return -ENOMEM;
+ vc4_crtc = &txp->base;
+ crtc = &vc4_crtc->base;
+
+ vc4_crtc->pdev = pdev;
+ vc4_crtc->data = &vc4_txp_crtc_data;
txp->pdev = pdev;
if (ret)
return ret;
+ ret = vc4_crtc_init(drm, vc4_crtc,
+ &vc4_txp_crtc_funcs, &vc4_txp_crtc_helper_funcs);
+ if (ret)
+ return ret;
+
+ encoder = &txp->connector.encoder;
+ encoder->possible_crtcs |= drm_crtc_mask(crtc);
+
ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0,
dev_name(dev), txp);
if (ret)