diff options
Diffstat (limited to 'drivers/gpu/drm/vc4')
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_bo.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_crtc.c | 495 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_drv.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_drv.h | 64 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_gem.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_hdmi.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_hvs.c | 291 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_kms.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_plane.c | 41 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_regs.h | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_txp.c | 109 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_v3d.c | 4 |
12 files changed, 627 insertions, 428 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 72d30d90b856..74ceebd62fbc 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -490,7 +490,7 @@ int vc4_dumb_create(struct drm_file *file_priv, bo->madv = VC4_MADV_WILLNEED; ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); - drm_gem_object_put_unlocked(&bo->base.base); + drm_gem_object_put(&bo->base.base); return ret; } @@ -834,7 +834,7 @@ int vc4_create_bo_ioctl(struct drm_device *dev, void *data, bo->madv = VC4_MADV_WILLNEED; ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); - drm_gem_object_put_unlocked(&bo->base.base); + drm_gem_object_put(&bo->base.base); return ret; } @@ -854,7 +854,7 @@ int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, /* The mmap offset was set up at BO allocation time. */ args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); - drm_gem_object_put_unlocked(gem_obj); + drm_gem_object_put(gem_obj); return 0; } @@ -918,7 +918,7 @@ vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); fail: - drm_gem_object_put_unlocked(&bo->base.base); + drm_gem_object_put(&bo->base.base); return ret; } @@ -965,7 +965,7 @@ int vc4_set_tiling_ioctl(struct drm_device *dev, void *data, bo = to_vc4_bo(gem_obj); bo->t_format = t_format; - drm_gem_object_put_unlocked(gem_obj); + drm_gem_object_put(gem_obj); return 0; } @@ -1000,7 +1000,7 @@ int vc4_get_tiling_ioctl(struct drm_device *dev, void *data, else args->modifier = DRM_FORMAT_MOD_NONE; - drm_gem_object_put_unlocked(gem_obj); + drm_gem_object_put(gem_obj); return 0; } @@ -1091,7 +1091,7 @@ int vc4_label_bo_ioctl(struct drm_device *dev, void *data, ret = -ENOMEM; mutex_unlock(&vc4->bo_lock); - drm_gem_object_put_unlocked(gem_obj); + drm_gem_object_put(gem_obj); return ret; } diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 1208258ad3b2..6d8fa6118fc1 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -44,26 +44,7 @@ #include "vc4_drv.h" #include "vc4_regs.h" -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; - - struct { - unsigned int left; - unsigned int right; - unsigned int top; - unsigned int bottom; - } margins; -}; - -static inline struct vc4_crtc_state * -to_vc4_crtc_state(struct drm_crtc_state *crtc_state) -{ - return (struct vc4_crtc_state *)crtc_state; -} +#define HVS_FIFO_LATENCY_PIX 6 #define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset)) #define CRTC_READ(offset) readl(vc4_crtc->regs + (offset)) @@ -203,67 +184,25 @@ static bool vc4_crtc_get_scanout_position(struct drm_crtc *crtc, return ret; } -static void vc4_crtc_destroy(struct drm_crtc *crtc) +void vc4_crtc_destroy(struct drm_crtc *crtc) { drm_crtc_cleanup(crtc); } -static void -vc4_crtc_lut_load(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); - u32 i; - - /* The LUT memory is laid out with each HVS channel in order, - * each of which takes 256 writes for R, 256 for G, then 256 - * for B. - */ - HVS_WRITE(SCALER_GAMADDR, - SCALER_GAMADDR_AUTOINC | - (vc4_crtc->channel * 3 * crtc->gamma_size)); - - for (i = 0; i < crtc->gamma_size; i++) - HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]); - for (i = 0; i < crtc->gamma_size; i++) - HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]); - for (i = 0; i < crtc->gamma_size; i++) - HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]); -} - -static void -vc4_crtc_update_gamma_lut(struct drm_crtc *crtc) -{ - struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - struct drm_color_lut *lut = crtc->state->gamma_lut->data; - u32 length = drm_color_lut_size(crtc->state->gamma_lut); - u32 i; - - for (i = 0; i < length; i++) { - vc4_crtc->lut_r[i] = drm_color_lut_extract(lut[i].red, 8); - vc4_crtc->lut_g[i] = drm_color_lut_extract(lut[i].green, 8); - vc4_crtc->lut_b[i] = drm_color_lut_extract(lut[i].blue, 8); - } - - vc4_crtc_lut_load(crtc); -} - static u32 vc4_get_fifo_full_level(u32 format) { static const u32 fifo_len_bytes = 64; - static const u32 hvs_latency_pix = 6; switch (format) { case PV_CONTROL_FORMAT_DSIV_16: case PV_CONTROL_FORMAT_DSIC_16: - return fifo_len_bytes - 2 * hvs_latency_pix; + return fifo_len_bytes - 2 * HVS_FIFO_LATENCY_PIX; case PV_CONTROL_FORMAT_DSIV_18: return fifo_len_bytes - 14; case PV_CONTROL_FORMAT_24: case PV_CONTROL_FORMAT_DSIV_24: default: - return fifo_len_bytes - 3 * hvs_latency_pix; + return fifo_len_bytes - 3 * HVS_FIFO_LATENCY_PIX; } } @@ -364,7 +303,8 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc) (is_dsi ? PV_VCONTROL_DSI : 0)); } - CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep); + if (is_dsi) + CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep); CRTC_WRITE(PV_CONTROL, VC4_SET_FIELD(format, PV_CONTROL_FORMAT) | @@ -382,12 +322,7 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc) 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) { @@ -397,42 +332,9 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) drm_print_regset32(&p, &vc4_crtc->regset); } - if (vc4_crtc->channel == 2) { - u32 dispctrl; - u32 dsp3_mux; + vc4_crtc_config_pv(crtc); - /* - * 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 | - SCALER_DISPBKGND_GAMMA | - (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); - - /* Reload the LUT, since the SRAMs would have been disabled if - * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once. - */ - vc4_crtc_lut_load(crtc); + vc4_hvs_mode_set_nofb(crtc); if (debug_dump_regs) { struct drm_printer p = drm_info_printer(&vc4_crtc->pdev->dev); @@ -454,10 +356,9 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct drm_device *dev = crtc->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - u32 chan = vc4_crtc->channel; int ret; + require_hvs_enabled(dev); /* Disable vblank irq handling before crtc is disabled. */ @@ -468,28 +369,7 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, ret = wait_for(!(CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN), 1); WARN_ONCE(ret, "Timeout waiting for !PV_VCONTROL_VIDEN\n"); - if (HVS_READ(SCALER_DISPCTRLX(chan)) & - SCALER_DISPCTRLX_ENABLE) { - HVS_WRITE(SCALER_DISPCTRLX(chan), - SCALER_DISPCTRLX_RESET); - - /* While the docs say that reset is self-clearing, it - * seems it doesn't actually. - */ - HVS_WRITE(SCALER_DISPCTRLX(chan), 0); - } - - /* Once we leave, the scaler should be disabled and its fifo empty. */ - - WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET); - - WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)), - SCALER_DISPSTATX_MODE) != - SCALER_DISPSTATX_MODE_DISABLED); - - WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) & - (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) != - SCALER_DISPSTATX_EMPTY); + vc4_hvs_atomic_disable(crtc, old_state); /* * Make sure we issue a vblank event after disabling the CRTC if @@ -505,52 +385,11 @@ 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; - 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); - - if (crtc->state->event) { - unsigned long flags; - - crtc->state->event->pipe = drm_crtc_index(crtc); - - WARN_ON(drm_crtc_vblank_get(crtc) != 0); - - spin_lock_irqsave(&dev->event_lock, flags); - - 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); - - spin_unlock_irqrestore(&dev->event_lock, flags); - } else { - HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), - vc4_state->mm.start); - } -} - static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { 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; require_hvs_enabled(dev); @@ -558,25 +397,14 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, * drm_crtc_get_vblank() fails in vc4_crtc_update_dlist(). */ drm_crtc_vblank_on(crtc); - vc4_crtc_update_dlist(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 | - (vc4_state->feed_txp ? SCALER_DISPCTRLX_ONESHOT : 0)); + vc4_hvs_atomic_enable(crtc, old_state); /* 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); + 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, @@ -627,31 +455,11 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); - struct drm_device *dev = crtc->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - 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, i; - /* The pixelvalve can only feed one encoder (and encoders are - * 1:1 with connectors.) - */ - if (hweight32(state->connector_mask) > 1) - return -EINVAL; - - drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, state) - dlist_count += vc4_plane_dlist_size(plane_state); - - dlist_count++; /* Account for SCALER_CTL0_END. */ - - spin_lock_irqsave(&vc4->hvs->mm_lock, flags); - ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm, - dlist_count); - spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags); + ret = vc4_hvs_atomic_check(crtc, state); if (ret) return ret; @@ -659,17 +467,6 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, 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; @@ -680,89 +477,6 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, return 0; } -static void vc4_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_state) -{ - 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_plane *plane; - struct vc4_plane_state *vc4_plane_state; - bool debug_dump_regs = false; - bool enable_bg_fill = false; - u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start; - u32 __iomem *dlist_next = dlist_start; - - if (debug_dump_regs) { - DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc)); - vc4_hvs_dump_state(dev); - } - - /* Copy all the active planes' dlist contents to the hardware dlist. */ - drm_atomic_crtc_for_each_plane(plane, crtc) { - /* Is this the first active plane? */ - if (dlist_next == dlist_start) { - /* We need to enable background fill when a plane - * could be alpha blending from the background, i.e. - * where no other plane is underneath. It suffices to - * consider the first active plane here since we set - * needs_bg_fill such that either the first plane - * already needs it or all planes on top blend from - * the first or a lower plane. - */ - vc4_plane_state = to_vc4_plane_state(plane->state); - enable_bg_fill = vc4_plane_state->needs_bg_fill; - } - - dlist_next += vc4_plane_write_dlist(plane, dlist_next); - } - - writel(SCALER_CTL0_END, dlist_next); - dlist_next++; - - WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size); - - if (enable_bg_fill) - /* This sets a black background color fill, as is the case - * with other DRM drivers. - */ - HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), - HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)) | - SCALER_DISPBKGND_FILL); - - /* Only update DISPLIST if the CRTC was already running and is not - * being disabled. - * vc4_crtc_enable() takes care of updating the dlist just after - * re-enabling VBLANK interrupts and before enabling the engine. - * If the CRTC is being disabled, there's no point in updating this - * information. - */ - if (crtc->state->active && old_state->active) - vc4_crtc_update_dlist(crtc); - - if (crtc->state->color_mgmt_changed) { - u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)); - - if (crtc->state->gamma_lut) { - vc4_crtc_update_gamma_lut(crtc); - dispbkgndx |= SCALER_DISPBKGND_GAMMA; - } else { - /* Unsetting DISPBKGND_GAMMA skips the gamma lut step - * in hardware, which is the same as a linear lut that - * DRM expects us to use in absence of a user lut. - */ - dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; - } - HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), dispbkgndx); - } - - if (debug_dump_regs) { - DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc)); - vc4_hvs_dump_state(dev); - } -} - static int vc4_enable_vblank(struct drm_crtc *crtc) { struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); @@ -961,11 +675,11 @@ static int vc4_async_page_flip(struct drm_crtc *crtc, return 0; } -static int vc4_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t flags, - struct drm_modeset_acquire_ctx *ctx) +int vc4_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t flags, + struct drm_modeset_acquire_ctx *ctx) { if (flags & DRM_MODE_PAGE_FLIP_ASYNC) return vc4_async_page_flip(crtc, fb, event, flags); @@ -973,7 +687,7 @@ static int vc4_page_flip(struct drm_crtc *crtc, return drm_atomic_helper_page_flip(crtc, fb, event, flags, ctx); } -static struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc) +struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc) { struct vc4_crtc_state *vc4_state, *old_vc4_state; @@ -989,8 +703,8 @@ static struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc) return &vc4_state->base; } -static void vc4_crtc_destroy_state(struct drm_crtc *crtc, - struct drm_crtc_state *state) +void vc4_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) { struct vc4_dev *vc4 = to_vc4_dev(crtc->dev); struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); @@ -1007,15 +721,13 @@ static void vc4_crtc_destroy_state(struct drm_crtc *crtc, drm_atomic_helper_crtc_destroy_state(crtc, state); } -static void -vc4_crtc_reset(struct drm_crtc *crtc) +void vc4_crtc_reset(struct drm_crtc *crtc) { if (crtc->state) vc4_crtc_destroy_state(crtc, crtc->state); - crtc->state = kzalloc(sizeof(struct vc4_crtc_state), GFP_KERNEL); if (crtc->state) - crtc->state->crtc = crtc; + __drm_atomic_helper_crtc_reset(crtc, crtc->state); } static const struct drm_crtc_funcs vc4_crtc_funcs = { @@ -1038,14 +750,16 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { .mode_set_nofb = vc4_crtc_mode_set_nofb, .mode_valid = vc4_crtc_mode_valid, .atomic_check = vc4_crtc_atomic_check, - .atomic_flush = vc4_crtc_atomic_flush, + .atomic_flush = vc4_hvs_atomic_flush, .atomic_enable = vc4_crtc_atomic_enable, .atomic_disable = vc4_crtc_atomic_disable, .get_scanout_position = vc4_crtc_get_scanout_position, }; -static const struct vc4_crtc_data pv0_data = { - .hvs_channel = 0, +static const struct vc4_pv_data bcm2835_pv0_data = { + .base = { + .hvs_channel = 0, + }, .debugfs_name = "crtc0_regs", .encoder_types = { [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI0, @@ -1053,8 +767,10 @@ static const struct vc4_crtc_data pv0_data = { }, }; -static const struct vc4_crtc_data pv1_data = { - .hvs_channel = 2, +static const struct vc4_pv_data bcm2835_pv1_data = { + .base = { + .hvs_channel = 2, + }, .debugfs_name = "crtc1_regs", .encoder_types = { [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI1, @@ -1062,8 +778,10 @@ static const struct vc4_crtc_data pv1_data = { }, }; -static const struct vc4_crtc_data pv2_data = { - .hvs_channel = 1, +static const struct vc4_pv_data bcm2835_pv2_data = { + .base = { + .hvs_channel = 1, + }, .debugfs_name = "crtc2_regs", .encoder_types = { [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI, @@ -1072,9 +790,9 @@ static const struct vc4_crtc_data pv2_data = { }; static const struct of_device_id vc4_crtc_dt_match[] = { - { .compatible = "brcm,bcm2835-pixelvalve0", .data = &pv0_data }, - { .compatible = "brcm,bcm2835-pixelvalve1", .data = &pv1_data }, - { .compatible = "brcm,bcm2835-pixelvalve2", .data = &pv2_data }, + { .compatible = "brcm,bcm2835-pixelvalve0", .data = &bcm2835_pv0_data }, + { .compatible = "brcm,bcm2835-pixelvalve1", .data = &bcm2835_pv1_data }, + { .compatible = "brcm,bcm2835-pixelvalve2", .data = &bcm2835_pv2_data }, {} }; @@ -1082,23 +800,16 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm, struct drm_crtc *crtc) { struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - const struct vc4_crtc_data *crtc_data = vc4_crtc->data; - const enum vc4_encoder_type *encoder_types = crtc_data->encoder_types; + 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; drm_for_each_encoder(encoder, drm) { 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++) { + for (i = 0; i < ARRAY_SIZE(pv_data->encoder_types); i++) { if (vc4_encoder->type == encoder_types[i]) { vc4_encoder->clock_select = i; encoder->possible_crtcs |= drm_crtc_mask(crtc); @@ -1124,34 +835,13 @@ vc4_crtc_get_cob_allocation(struct vc4_crtc *vc4_crtc) vc4_crtc->cob_size = top - base + 4; } -static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) +int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, + const struct drm_crtc_funcs *crtc_funcs, + const struct drm_crtc_helper_funcs *crtc_helper_funcs) { - struct platform_device *pdev = to_platform_device(dev); - struct drm_device *drm = dev_get_drvdata(master); - struct vc4_crtc *vc4_crtc; - struct drm_crtc *crtc; - struct drm_plane *primary_plane, *cursor_plane, *destroy_plane, *temp; - const struct of_device_id *match; - int ret, i; - - vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL); - if (!vc4_crtc) - return -ENOMEM; - crtc = &vc4_crtc->base; - - match = of_match_device(vc4_crtc_dt_match, dev); - if (!match) - return -ENODEV; - vc4_crtc->data = match->data; - vc4_crtc->pdev = pdev; - - vc4_crtc->regs = vc4_ioremap_regs(pdev, 0); - if (IS_ERR(vc4_crtc->regs)) - return PTR_ERR(vc4_crtc->regs); - - vc4_crtc->regset.base = vc4_crtc->regs; - vc4_crtc->regset.regs = crtc_regs; - vc4_crtc->regset.nregs = ARRAY_SIZE(crtc_regs); + struct drm_crtc *crtc = &vc4_crtc->base; + struct drm_plane *primary_plane; + unsigned int i; /* For now, we create just the primary and the legacy cursor * planes. We should be able to stack more planes on easily, @@ -1161,14 +851,13 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) */ primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY); if (IS_ERR(primary_plane)) { - dev_err(dev, "failed to construct primary plane\n"); - ret = PTR_ERR(primary_plane); - goto err; + dev_err(drm->dev, "failed to construct primary plane\n"); + return PTR_ERR(primary_plane); } drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, - &vc4_crtc_funcs, NULL); - drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs); + crtc_funcs, NULL); + drm_crtc_helper_add(crtc, crtc_helper_funcs); vc4_crtc->channel = vc4_crtc->data->hvs_channel; drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size); @@ -1177,37 +866,51 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) * implemented as private driver state in vc4_kms, not here. */ drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size); + vc4_crtc_get_cob_allocation(vc4_crtc); - /* Set up some arbitrary number of planes. We're not limited - * by a set number of physical registers, just the space in - * the HVS (16k) and how small an plane can be (28 bytes). - * However, each plane we set up takes up some memory, and - * increases the cost of looping over planes, which atomic - * modesetting does quite a bit. As a result, we pick a - * modest number of planes to expose, that should hopefully - * still cover any sane usecase. - */ - for (i = 0; i < 8; i++) { - struct drm_plane *plane = - vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY); + for (i = 0; i < crtc->gamma_size; i++) { + vc4_crtc->lut_r[i] = i; + vc4_crtc->lut_g[i] = i; + vc4_crtc->lut_b[i] = i; + } - if (IS_ERR(plane)) - continue; + return 0; +} - plane->possible_crtcs = drm_crtc_mask(crtc); - } +static int vc4_crtc_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); + const struct vc4_pv_data *pv_data; + struct vc4_crtc *vc4_crtc; + struct drm_crtc *crtc; + struct drm_plane *destroy_plane, *temp; + int ret; - /* Set up the legacy cursor after overlay initialization, - * since we overlay planes on the CRTC in the order they were - * initialized. - */ - cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR); - if (!IS_ERR(cursor_plane)) { - cursor_plane->possible_crtcs = drm_crtc_mask(crtc); - crtc->cursor = cursor_plane; - } + vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL); + if (!vc4_crtc) + return -ENOMEM; + crtc = &vc4_crtc->base; - vc4_crtc_get_cob_allocation(vc4_crtc); + pv_data = of_device_get_match_data(dev); + if (!pv_data) + return -ENODEV; + vc4_crtc->data = &pv_data->base; + vc4_crtc->pdev = pdev; + + vc4_crtc->regs = vc4_ioremap_regs(pdev, 0); + if (IS_ERR(vc4_crtc->regs)) + return PTR_ERR(vc4_crtc->regs); + + vc4_crtc->regset.base = vc4_crtc->regs; + vc4_crtc->regset.regs = crtc_regs; + vc4_crtc->regset.nregs = ARRAY_SIZE(crtc_regs); + + ret = vc4_crtc_init(drm, vc4_crtc, + &vc4_crtc_funcs, &vc4_crtc_helper_funcs); + if (ret) + return ret; + vc4_set_crtc_possible_masks(drm, crtc); CRTC_WRITE(PV_INTEN, 0); CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START); @@ -1216,17 +919,9 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) if (ret) goto err_destroy_planes; - vc4_set_crtc_possible_masks(drm, crtc); - - for (i = 0; i < crtc->gamma_size; i++) { - vc4_crtc->lut_r[i] = i; - vc4_crtc->lut_g[i] = i; - vc4_crtc->lut_b[i] = i; - } - platform_set_drvdata(pdev, vc4_crtc); - vc4_debugfs_add_regset32(drm, vc4_crtc->data->debugfs_name, + vc4_debugfs_add_regset32(drm, pv_data->debugfs_name, &vc4_crtc->regset); return 0; @@ -1237,7 +932,7 @@ err_destroy_planes: if (destroy_plane->possible_crtcs == drm_crtc_mask(crtc)) destroy_plane->funcs->destroy(destroy_plane); } -err: + return ret; } diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 76f93b662766..38343d2fb4fb 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -288,6 +288,10 @@ static int vc4_drm_bind(struct device *dev) if (ret) goto gem_destroy; + ret = vc4_plane_create_additional_planes(drm); + if (ret) + goto unbind_all; + drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false); ret = vc4_kms_load(drm); @@ -339,8 +343,8 @@ static struct platform_driver *const component_drivers[] = { &vc4_vec_driver, &vc4_dpi_driver, &vc4_dsi_driver, - &vc4_txp_driver, &vc4_hvs_driver, + &vc4_txp_driver, &vc4_crtc_driver, &vc4_v3d_driver, }; diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 3b1f02efefbe..fa19160c801f 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -2,6 +2,8 @@ /* * Copyright (C) 2015 Broadcom */ +#ifndef _VC4_DRV_H_ +#define _VC4_DRV_H_ #include <linux/delay.h> #include <linux/refcount.h> @@ -441,9 +443,14 @@ to_vc4_encoder(struct drm_encoder *encoder) struct vc4_crtc_data { /* Which channel of the HVS this pixelvalve sources from. */ int hvs_channel; +}; + +struct vc4_pv_data { + struct vc4_crtc_data base; enum vc4_encoder_type encoder_types[4]; const char *debugfs_name; + }; struct vc4_crtc { @@ -475,6 +482,41 @@ to_vc4_crtc(struct drm_crtc *crtc) return (struct vc4_crtc *)crtc; } +static inline const struct vc4_crtc_data * +vc4_crtc_to_vc4_crtc_data(const struct vc4_crtc *crtc) +{ + return crtc->data; +} + +static inline const struct vc4_pv_data * +vc4_crtc_to_vc4_pv_data(const struct vc4_crtc *crtc) +{ + const struct vc4_crtc_data *data = vc4_crtc_to_vc4_crtc_data(crtc); + + return container_of(data, struct vc4_pv_data, base); +} + +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; + + struct { + unsigned int left; + unsigned int right; + unsigned int top; + unsigned int bottom; + } margins; +}; + +static inline struct vc4_crtc_state * +to_vc4_crtc_state(struct drm_crtc_state *crtc_state) +{ + return (struct vc4_crtc_state *)crtc_state; +} + #define V3D_READ(offset) readl(vc4->v3d->regs + offset) #define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset) #define HVS_READ(offset) readl(vc4->hvs->regs + offset) @@ -752,8 +794,20 @@ void vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo); /* vc4_crtc.c */ extern struct platform_driver vc4_crtc_driver; +int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, + const struct drm_crtc_funcs *crtc_funcs, + const struct drm_crtc_helper_funcs *crtc_helper_funcs); +void vc4_crtc_destroy(struct drm_crtc *crtc); +int vc4_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t flags, + struct drm_modeset_acquire_ctx *ctx); +struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc); +void vc4_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); +void vc4_crtc_reset(struct drm_crtc *crtc); void vc4_crtc_handle_vblank(struct vc4_crtc *crtc); -void vc4_crtc_txp_armed(struct drm_crtc_state *state); void vc4_crtc_get_margins(struct drm_crtc_state *state, unsigned int *right, unsigned int *left, unsigned int *top, unsigned int *bottom); @@ -834,6 +888,11 @@ void vc4_irq_reset(struct drm_device *dev); /* vc4_hvs.c */ extern struct platform_driver vc4_hvs_driver; +int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state); +void vc4_hvs_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state); +void vc4_hvs_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state); +void vc4_hvs_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *state); +void vc4_hvs_mode_set_nofb(struct drm_crtc *crtc); void vc4_hvs_dump_state(struct drm_device *dev); void vc4_hvs_unmask_underrun(struct drm_device *dev, int channel); void vc4_hvs_mask_underrun(struct drm_device *dev, int channel); @@ -844,6 +903,7 @@ int vc4_kms_load(struct drm_device *dev); /* vc4_plane.c */ struct drm_plane *vc4_plane_init(struct drm_device *dev, enum drm_plane_type type); +int vc4_plane_create_additional_planes(struct drm_device *dev); u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); u32 vc4_plane_dlist_size(const struct drm_plane_state *state); void vc4_plane_async_set_fb(struct drm_plane *plane, @@ -897,3 +957,5 @@ int vc4_perfmon_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int vc4_perfmon_get_values_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); + +#endif /* _VC4_DRV_H_ */ diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index e1cfc3ccd05a..9f01ddd5b932 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -58,7 +58,7 @@ vc4_free_hang_state(struct drm_device *dev, struct vc4_hang_state *state) unsigned int i; for (i = 0; i < state->user_state.bo_count; i++) - drm_gem_object_put_unlocked(state->bo[i]); + drm_gem_object_put(state->bo[i]); kfree(state); } @@ -808,7 +808,7 @@ fail_dec_usecnt: fail_put_bo: /* Release any reference to acquired objects. */ for (i = 0; i < exec->bo_count && exec->bo[i]; i++) - drm_gem_object_put_unlocked(&exec->bo[i]->base); + drm_gem_object_put(&exec->bo[i]->base); fail: kvfree(handles); @@ -957,7 +957,7 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec) struct vc4_bo *bo = to_vc4_bo(&exec->bo[i]->base); vc4_bo_dec_usecnt(bo); - drm_gem_object_put_unlocked(&exec->bo[i]->base); + drm_gem_object_put(&exec->bo[i]->base); } kvfree(exec->bo); } @@ -966,7 +966,7 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec) struct vc4_bo *bo = list_first_entry(&exec->unref_list, struct vc4_bo, unref_head); list_del(&bo->unref_head); - drm_gem_object_put_unlocked(&bo->base.base); + drm_gem_object_put(&bo->base.base); } /* Free up the allocation of any bin slots we used. */ @@ -1107,7 +1107,7 @@ vc4_wait_bo_ioctl(struct drm_device *dev, void *data, ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno, &args->timeout_ns); - drm_gem_object_put_unlocked(gem_obj); + drm_gem_object_put(gem_obj); return ret; } @@ -1301,7 +1301,7 @@ vc4_gem_destroy(struct drm_device *dev) * the overflow allocation registers. Now free the object. */ if (vc4->bin_bo) { - drm_gem_object_put_unlocked(&vc4->bin_bo->base.base); + drm_gem_object_put(&vc4->bin_bo->base.base); vc4->bin_bo = NULL; } @@ -1382,7 +1382,7 @@ int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data, ret = 0; out_put_gem: - drm_gem_object_put_unlocked(gem_obj); + drm_gem_object_put(gem_obj); return ret; } diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 625bfcf52dc4..15a11cd4de25 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -1330,8 +1330,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) hdmi->pixel_clock = devm_clk_get(dev, "pixel"); if (IS_ERR(hdmi->pixel_clock)) { - DRM_ERROR("Failed to get pixel clock\n"); - return PTR_ERR(hdmi->pixel_clock); + ret = PTR_ERR(hdmi->pixel_clock); + if (ret != -EPROBE_DEFER) + DRM_ERROR("Failed to get pixel clock\n"); + return ret; } hdmi->hsm_clock = devm_clk_get(dev, "hdmi"); if (IS_ERR(hdmi->hsm_clock)) { diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 5a43659da319..2d2bf59c0503 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -23,6 +23,7 @@ #include <linux/platform_device.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_vblank.h> #include "vc4_drv.h" #include "vc4_regs.h" @@ -154,6 +155,296 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, return 0; } +static void vc4_hvs_lut_load(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); + u32 i; + + /* The LUT memory is laid out with each HVS channel in order, + * each of which takes 256 writes for R, 256 for G, then 256 + * for B. + */ + HVS_WRITE(SCALER_GAMADDR, + SCALER_GAMADDR_AUTOINC | + (vc4_crtc->channel * 3 * crtc->gamma_size)); + + for (i = 0; i < crtc->gamma_size; i++) + HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]); + for (i = 0; i < crtc->gamma_size; i++) + HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]); + for (i = 0; i < crtc->gamma_size; i++) + HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]); +} + +static void vc4_hvs_update_gamma_lut(struct drm_crtc *crtc) +{ + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct drm_color_lut *lut = crtc->state->gamma_lut->data; + u32 length = drm_color_lut_size(crtc->state->gamma_lut); + u32 i; + + for (i = 0; i < length; i++) { + vc4_crtc->lut_r[i] = drm_color_lut_extract(lut[i].red, 8); + vc4_crtc->lut_g[i] = drm_color_lut_extract(lut[i].green, 8); + vc4_crtc->lut_b[i] = drm_color_lut_extract(lut[i].blue, 8); + } + + vc4_hvs_lut_load(crtc); +} + +int vc4_hvs_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_plane *plane; + unsigned long flags; + const struct drm_plane_state *plane_state; + u32 dlist_count = 0; + int ret; + + /* The pixelvalve can only feed one encoder (and encoders are + * 1:1 with connectors.) + */ + if (hweight32(state->connector_mask) > 1) + return -EINVAL; + + drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, state) + dlist_count += vc4_plane_dlist_size(plane_state); + + dlist_count++; /* Account for SCALER_CTL0_END. */ + + spin_lock_irqsave(&vc4->hvs->mm_lock, flags); + ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm, + dlist_count); + spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags); + if (ret) + return ret; + + return 0; +} + +static void vc4_hvs_update_dlist(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); + + if (crtc->state->event) { + unsigned long flags; + + crtc->state->event->pipe = drm_crtc_index(crtc); + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irqsave(&dev->event_lock, flags); + + 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); + + spin_unlock_irqrestore(&dev->event_lock, flags); + } else { + HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), + vc4_state->mm.start); + } +} + +void vc4_hvs_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + 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 oneshot = vc4_state->feed_txp; + u32 dispctrl; + + vc4_hvs_update_dlist(crtc); + + /* Turn on the scaler, which will wait for vstart to start + * compositing. + * When feeding the transposer, we should operate in oneshot + * mode. + */ + dispctrl = SCALER_DISPCTRLX_ENABLE; + dispctrl |= VC4_SET_FIELD(mode->hdisplay, + SCALER_DISPCTRLX_WIDTH) | + VC4_SET_FIELD(mode->vdisplay, + SCALER_DISPCTRLX_HEIGHT) | + (oneshot ? SCALER_DISPCTRLX_ONESHOT : 0); + + HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), dispctrl); +} + +void vc4_hvs_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + u32 chan = vc4_crtc->channel; + + if (HVS_READ(SCALER_DISPCTRLX(chan)) & + SCALER_DISPCTRLX_ENABLE) { + HVS_WRITE(SCALER_DISPCTRLX(chan), + SCALER_DISPCTRLX_RESET); + + /* While the docs say that reset is self-clearing, it + * seems it doesn't actually. + */ + HVS_WRITE(SCALER_DISPCTRLX(chan), 0); + } + + /* Once we leave, the scaler should be disabled and its fifo empty. */ + + WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET); + + WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)), + SCALER_DISPSTATX_MODE) != + SCALER_DISPSTATX_MODE_DISABLED); + + WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) & + (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) != + SCALER_DISPSTATX_EMPTY); +} + +void vc4_hvs_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + 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_plane *plane; + struct vc4_plane_state *vc4_plane_state; + bool debug_dump_regs = false; + bool enable_bg_fill = false; + u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start; + u32 __iomem *dlist_next = dlist_start; + + if (debug_dump_regs) { + DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc)); + vc4_hvs_dump_state(dev); + } + + /* Copy all the active planes' dlist contents to the hardware dlist. */ + drm_atomic_crtc_for_each_plane(plane, crtc) { + /* Is this the first active plane? */ + if (dlist_next == dlist_start) { + /* We need to enable background fill when a plane + * could be alpha blending from the background, i.e. + * where no other plane is underneath. It suffices to + * consider the first active plane here since we set + * needs_bg_fill such that either the first plane + * already needs it or all planes on top blend from + * the first or a lower plane. + */ + vc4_plane_state = to_vc4_plane_state(plane->state); + enable_bg_fill = vc4_plane_state->needs_bg_fill; + } + + dlist_next += vc4_plane_write_dlist(plane, dlist_next); + } + + writel(SCALER_CTL0_END, dlist_next); + dlist_next++; + + WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size); + + if (enable_bg_fill) + /* This sets a black background color fill, as is the case + * with other DRM drivers. + */ + HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), + HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)) | + SCALER_DISPBKGND_FILL); + + /* Only update DISPLIST if the CRTC was already running and is not + * being disabled. + * vc4_crtc_enable() takes care of updating the dlist just after + * re-enabling VBLANK interrupts and before enabling the engine. + * If the CRTC is being disabled, there's no point in updating this + * information. + */ + if (crtc->state->active && old_state->active) + vc4_hvs_update_dlist(crtc); + + if (crtc->state->color_mgmt_changed) { + u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)); + + if (crtc->state->gamma_lut) { + vc4_hvs_update_gamma_lut(crtc); + dispbkgndx |= SCALER_DISPBKGND_GAMMA; + } else { + /* Unsetting DISPBKGND_GAMMA skips the gamma lut step + * in hardware, which is the same as a linear lut that + * DRM expects us to use in absence of a user lut. + */ + dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; + } + HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), dispbkgndx); + } + + if (debug_dump_regs) { + DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc)); + vc4_hvs_dump_state(dev); + } +} + +void vc4_hvs_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; + + if (vc4_crtc->data->hvs_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); + } + + HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), + SCALER_DISPBKGND_AUTOHS | + SCALER_DISPBKGND_GAMMA | + (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); + + /* Reload the LUT, since the SRAMs would have been disabled if + * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once. + */ + vc4_hvs_lut_load(crtc); +} + void vc4_hvs_mask_underrun(struct drm_device *dev, int channel) { struct vc4_dev *vc4 = to_vc4_dev(dev); diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 78d4fb0499e3..08318e69061b 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -330,7 +330,7 @@ static struct drm_framebuffer *vc4_fb_create(struct drm_device *dev, mode_cmd_local.modifier[0] = DRM_FORMAT_MOD_NONE; } - drm_gem_object_put_unlocked(gem_obj); + drm_gem_object_put(gem_obj); mode_cmd = &mode_cmd_local; } diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 91e408f7a56e..d040d9f12c6d 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -1267,3 +1267,44 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, return plane; } + +int vc4_plane_create_additional_planes(struct drm_device *drm) +{ + struct drm_plane *cursor_plane; + struct drm_crtc *crtc; + unsigned int i; + + /* Set up some arbitrary number of planes. We're not limited + * by a set number of physical registers, just the space in + * the HVS (16k) and how small an plane can be (28 bytes). + * However, each plane we set up takes up some memory, and + * increases the cost of looping over planes, which atomic + * modesetting does quite a bit. As a result, we pick a + * modest number of planes to expose, that should hopefully + * still cover any sane usecase. + */ + for (i = 0; i < 8; i++) { + struct drm_plane *plane = + vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY); + + if (IS_ERR(plane)) + continue; + + plane->possible_crtcs = + GENMASK(drm->mode_config.num_crtc - 1, 0); + } + + drm_for_each_crtc(crtc, drm) { + /* Set up the legacy cursor after overlay initialization, + * since we overlay planes on the CRTC in the order they were + * initialized. + */ + cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR); + if (!IS_ERR(cursor_plane)) { + cursor_plane->possible_crtcs = drm_crtc_mask(crtc); + crtc->cursor = cursor_plane; + } + } + + return 0; +} diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index b5a6b4cdd332..324462cc9cd4 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -6,19 +6,18 @@ #ifndef VC4_REGS_H #define VC4_REGS_H +#include <linux/bitfield.h> #include <linux/bitops.h> #define VC4_MASK(high, low) ((u32)GENMASK(high, low)) /* Using the GNU statement expression extension */ #define VC4_SET_FIELD(value, field) \ ({ \ - uint32_t fieldval = (value) << field##_SHIFT; \ - WARN_ON((fieldval & ~field##_MASK) != 0); \ - fieldval & field##_MASK; \ + WARN_ON(!FIELD_FIT(field##_MASK, value)); \ + FIELD_PREP(field##_MASK, value); \ }) -#define VC4_GET_FIELD(word, field) (((word) & field##_MASK) >> \ - field##_SHIFT) +#define VC4_GET_FIELD(word, field) FIELD_GET(field##_MASK, word) #define V3D_IDENT0 0x00000 # define V3D_EXPECTED_IDENT0 \ diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index bf720206727f..a7c3af0005a0 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -19,6 +19,7 @@ #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" @@ -145,6 +146,8 @@ #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; @@ -222,6 +225,13 @@ static const u32 txp_fmts[] = { TXP_FORMAT_BGRA8888, }; +static void vc4_txp_armed(struct drm_crtc_state *state) +{ + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); + + vc4_state->txp_armed = true; +} + static int vc4_txp_connector_atomic_check(struct drm_connector *conn, struct drm_atomic_state *state) { @@ -256,7 +266,7 @@ static int vc4_txp_connector_atomic_check(struct drm_connector *conn, if (fb->pitches[0] & GENMASK(3, 0)) return -EINVAL; - vc4_crtc_txp_armed(crtc_state); + vc4_txp_armed(crtc_state); return 0; } @@ -355,23 +365,105 @@ static const struct drm_encoder_helper_funcs vc4_txp_encoder_helper_funcs = { .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); @@ -381,6 +473,11 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) 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; @@ -400,6 +497,14 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) 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) diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index cea77a21b205..f7ab979721b3 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -308,7 +308,7 @@ static int bin_bo_alloc(struct vc4_dev *vc4) struct vc4_bo, unref_head); list_del(&bo->unref_head); - drm_gem_object_put_unlocked(&bo->base.base); + drm_gem_object_put(&bo->base.base); } return ret; @@ -344,7 +344,7 @@ static void bin_bo_release(struct kref *ref) if (WARN_ON_ONCE(!vc4->bin_bo)) return; - drm_gem_object_put_unlocked(&vc4->bin_bo->base.base); + drm_gem_object_put(&vc4->bin_bo->base.base); vc4->bin_bo = NULL; } |