Lines Matching +full:bcm2835 +full:- +full:hvs

1 // SPDX-License-Identifier: GPL-2.0-only
7 * DOC: VC4 HVS module.
9 * The Hardware Video Scaler (HVS) is the piece of hardware that does
16 * There is a single global HVS, with multiple output FIFOs that can
18 * the HVS, while the vc4_crtc.c code actually drives HVS setup for
31 #include <soc/bcm2835/raspberrypi-firmware.h>
70 void vc4_hvs_dump_state(struct vc4_hvs *hvs) in vc4_hvs_dump_state() argument
72 struct drm_device *drm = &hvs->vc4->base; in vc4_hvs_dump_state()
73 struct drm_printer p = drm_info_printer(&hvs->pdev->dev); in vc4_hvs_dump_state()
79 drm_print_regset32(&p, &hvs->regset); in vc4_hvs_dump_state()
81 DRM_INFO("HVS ctx:\n"); in vc4_hvs_dump_state()
85 readl((u32 __iomem *)hvs->dlist + i + 0), in vc4_hvs_dump_state()
86 readl((u32 __iomem *)hvs->dlist + i + 1), in vc4_hvs_dump_state()
87 readl((u32 __iomem *)hvs->dlist + i + 2), in vc4_hvs_dump_state()
88 readl((u32 __iomem *)hvs->dlist + i + 3)); in vc4_hvs_dump_state()
96 struct drm_debugfs_entry *entry = m->private; in vc4_hvs_debugfs_underrun()
97 struct drm_device *dev = entry->dev; in vc4_hvs_debugfs_underrun()
101 drm_printf(&p, "%d\n", atomic_read(&vc4->underrun)); in vc4_hvs_debugfs_underrun()
108 struct drm_debugfs_entry *entry = m->private; in vc4_hvs_debugfs_dlist()
109 struct drm_device *dev = entry->dev; in vc4_hvs_debugfs_dlist()
111 struct vc4_hvs *hvs = vc4->hvs; in vc4_hvs_debugfs_dlist() local
122 drm_printf(&p, "HVS chan %u disabled\n", i); in vc4_hvs_debugfs_dlist()
126 drm_printf(&p, "HVS chan %u:\n", i); in vc4_hvs_debugfs_dlist()
129 dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j); in vc4_hvs_debugfs_dlist()
146 /* The filter kernel is composed of dwords each containing 3 9-bit
155 /* The whole filter kernel is arranged as the coefficients 0-16 going
156 * up, then a pad, then 17-31 going down and reversed within the
171 #define VC4_KERNEL_DWORDS (VC4_LINEAR_PHASE_KERNEL_DWORDS * 2 - 1)
177 VC4_LINEAR_PHASE_KERNEL(0, -2, -6, -8, -10, -8, -3, 2, 18,
180 static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, in vc4_hvs_upload_linear_kernel() argument
192 ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS); in vc4_hvs_upload_linear_kernel()
194 drm_err(&hvs->vc4->base, "Failed to allocate space for filter kernel: %d\n", in vc4_hvs_upload_linear_kernel()
199 dst_kernel = hvs->dlist + space->start; in vc4_hvs_upload_linear_kernel()
205 writel(kernel[VC4_KERNEL_DWORDS - i - 1], in vc4_hvs_upload_linear_kernel()
213 static void vc4_hvs_lut_load(struct vc4_hvs *hvs, in vc4_hvs_lut_load() argument
216 struct drm_device *drm = &hvs->vc4->base; in vc4_hvs_lut_load()
217 struct drm_crtc *crtc = &vc4_crtc->base; in vc4_hvs_lut_load()
218 struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); in vc4_hvs_lut_load()
225 /* The LUT memory is laid out with each HVS channel in order, in vc4_hvs_lut_load()
231 (vc4_state->assigned_channel * 3 * crtc->gamma_size)); in vc4_hvs_lut_load()
233 for (i = 0; i < crtc->gamma_size; i++) in vc4_hvs_lut_load()
234 HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]); in vc4_hvs_lut_load()
235 for (i = 0; i < crtc->gamma_size; i++) in vc4_hvs_lut_load()
236 HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]); in vc4_hvs_lut_load()
237 for (i = 0; i < crtc->gamma_size; i++) in vc4_hvs_lut_load()
238 HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]); in vc4_hvs_lut_load()
243 static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs, in vc4_hvs_update_gamma_lut() argument
246 struct drm_crtc_state *crtc_state = vc4_crtc->base.state; in vc4_hvs_update_gamma_lut()
247 struct drm_color_lut *lut = crtc_state->gamma_lut->data; in vc4_hvs_update_gamma_lut()
248 u32 length = drm_color_lut_size(crtc_state->gamma_lut); in vc4_hvs_update_gamma_lut()
252 vc4_crtc->lut_r[i] = drm_color_lut_extract(lut[i].red, 8); in vc4_hvs_update_gamma_lut()
253 vc4_crtc->lut_g[i] = drm_color_lut_extract(lut[i].green, 8); in vc4_hvs_update_gamma_lut()
254 vc4_crtc->lut_b[i] = drm_color_lut_extract(lut[i].blue, 8); in vc4_hvs_update_gamma_lut()
257 vc4_hvs_lut_load(hvs, vc4_crtc); in vc4_hvs_update_gamma_lut()
260 u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo) in vc4_hvs_get_fifo_frame_count() argument
262 struct drm_device *drm = &hvs->vc4->base; in vc4_hvs_get_fifo_frame_count()
288 int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output) in vc4_hvs_get_fifo_from_output() argument
290 struct vc4_dev *vc4 = hvs->vc4; in vc4_hvs_get_fifo_from_output()
294 if (!vc4->is_vc5) in vc4_hvs_get_fifo_from_output()
322 return -EPIPE; in vc4_hvs_get_fifo_from_output()
330 return -EPIPE; in vc4_hvs_get_fifo_from_output()
338 return -EPIPE; in vc4_hvs_get_fifo_from_output()
343 return -EPIPE; in vc4_hvs_get_fifo_from_output()
347 static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, in vc4_hvs_init_channel() argument
350 struct vc4_dev *vc4 = hvs->vc4; in vc4_hvs_init_channel()
351 struct drm_device *drm = &vc4->base; in vc4_hvs_init_channel()
353 struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state); in vc4_hvs_init_channel()
354 unsigned int chan = vc4_crtc_state->assigned_channel; in vc4_hvs_init_channel()
355 bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; in vc4_hvs_init_channel()
361 return -ENODEV; in vc4_hvs_init_channel()
375 if (!vc4->is_vc5) { in vc4_hvs_init_channel()
376 dispctrl |= VC4_SET_FIELD(mode->hdisplay, in vc4_hvs_init_channel()
378 VC4_SET_FIELD(mode->vdisplay, in vc4_hvs_init_channel()
383 dispctrl |= VC4_SET_FIELD(mode->hdisplay, in vc4_hvs_init_channel()
385 VC4_SET_FIELD(mode->vdisplay, in vc4_hvs_init_channel()
397 ((!vc4->is_vc5) ? SCALER_DISPBKGND_GAMMA : 0) | in vc4_hvs_init_channel()
403 vc4_hvs_lut_load(hvs, vc4_crtc); in vc4_hvs_init_channel()
410 void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan) in vc4_hvs_stop_channel() argument
412 struct drm_device *drm = &hvs->vc4->base; in vc4_hvs_stop_channel()
445 struct drm_device *dev = crtc->dev; in vc4_hvs_atomic_check()
456 if (hweight32(crtc_state->connector_mask) > 1) in vc4_hvs_atomic_check()
457 return -EINVAL; in vc4_hvs_atomic_check()
464 spin_lock_irqsave(&vc4->hvs->mm_lock, flags); in vc4_hvs_atomic_check()
465 ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm, in vc4_hvs_atomic_check()
467 spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags); in vc4_hvs_atomic_check()
476 struct drm_device *dev = crtc->dev; in vc4_hvs_install_dlist()
478 struct vc4_hvs *hvs = vc4->hvs; in vc4_hvs_install_dlist() local
479 struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); in vc4_hvs_install_dlist()
485 HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel), in vc4_hvs_install_dlist()
486 vc4_state->mm.start); in vc4_hvs_install_dlist()
493 struct drm_device *dev = crtc->dev; in vc4_hvs_update_dlist()
495 struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); in vc4_hvs_update_dlist()
498 if (crtc->state->event) { in vc4_hvs_update_dlist()
499 crtc->state->event->pipe = drm_crtc_index(crtc); in vc4_hvs_update_dlist()
503 spin_lock_irqsave(&dev->event_lock, flags); in vc4_hvs_update_dlist()
505 if (!vc4_crtc->feeds_txp || vc4_state->txp_armed) { in vc4_hvs_update_dlist()
506 vc4_crtc->event = crtc->state->event; in vc4_hvs_update_dlist()
507 crtc->state->event = NULL; in vc4_hvs_update_dlist()
510 spin_unlock_irqrestore(&dev->event_lock, flags); in vc4_hvs_update_dlist()
513 spin_lock_irqsave(&vc4_crtc->irq_lock, flags); in vc4_hvs_update_dlist()
514 vc4_crtc->current_dlist = vc4_state->mm.start; in vc4_hvs_update_dlist()
515 spin_unlock_irqrestore(&vc4_crtc->irq_lock, flags); in vc4_hvs_update_dlist()
522 struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); in vc4_hvs_atomic_begin()
525 spin_lock_irqsave(&vc4_crtc->irq_lock, flags); in vc4_hvs_atomic_begin()
526 vc4_crtc->current_hvs_channel = vc4_state->assigned_channel; in vc4_hvs_atomic_begin()
527 spin_unlock_irqrestore(&vc4_crtc->irq_lock, flags); in vc4_hvs_atomic_begin()
533 struct drm_device *dev = crtc->dev; in vc4_hvs_atomic_enable()
535 struct drm_display_mode *mode = &crtc->state->adjusted_mode; in vc4_hvs_atomic_enable()
537 bool oneshot = vc4_crtc->feeds_txp; in vc4_hvs_atomic_enable()
541 vc4_hvs_init_channel(vc4->hvs, crtc, mode, oneshot); in vc4_hvs_atomic_enable()
547 struct drm_device *dev = crtc->dev; in vc4_hvs_atomic_disable()
551 unsigned int chan = vc4_state->assigned_channel; in vc4_hvs_atomic_disable()
553 vc4_hvs_stop_channel(vc4->hvs, chan); in vc4_hvs_atomic_disable()
561 struct drm_device *dev = crtc->dev; in vc4_hvs_atomic_flush()
563 struct vc4_hvs *hvs = vc4->hvs; in vc4_hvs_atomic_flush() local
565 struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); in vc4_hvs_atomic_flush()
566 unsigned int channel = vc4_state->assigned_channel; in vc4_hvs_atomic_flush()
571 u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start; in vc4_hvs_atomic_flush()
582 if (vc4_state->assigned_channel == VC4_HVS_CHANNEL_DISABLED) in vc4_hvs_atomic_flush()
586 DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc)); in vc4_hvs_atomic_flush()
587 vc4_hvs_dump_state(hvs); in vc4_hvs_atomic_flush()
595 if (plane->state->normalized_zpos != zpos) in vc4_hvs_atomic_flush()
608 vc4_plane_state = to_vc4_plane_state(plane->state); in vc4_hvs_atomic_flush()
609 enable_bg_fill = vc4_plane_state->needs_bg_fill; in vc4_hvs_atomic_flush()
623 WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size); in vc4_hvs_atomic_flush()
636 * re-enabling VBLANK interrupts and before enabling the engine. in vc4_hvs_atomic_flush()
640 if (crtc->state->active && old_state->active) { in vc4_hvs_atomic_flush()
645 if (crtc->state->color_mgmt_changed) { in vc4_hvs_atomic_flush()
648 if (crtc->state->gamma_lut) { in vc4_hvs_atomic_flush()
649 vc4_hvs_update_gamma_lut(hvs, vc4_crtc); in vc4_hvs_atomic_flush()
662 DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc)); in vc4_hvs_atomic_flush()
663 vc4_hvs_dump_state(hvs); in vc4_hvs_atomic_flush()
669 void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel) in vc4_hvs_mask_underrun() argument
671 struct drm_device *drm = &hvs->vc4->base; in vc4_hvs_mask_underrun()
679 dispctrl &= ~(hvs->vc4->is_vc5 ? SCALER5_DISPCTRL_DSPEISLUR(channel) : in vc4_hvs_mask_underrun()
687 void vc4_hvs_unmask_underrun(struct vc4_hvs *hvs, int channel) in vc4_hvs_unmask_underrun() argument
689 struct drm_device *drm = &hvs->vc4->base; in vc4_hvs_unmask_underrun()
697 dispctrl |= (hvs->vc4->is_vc5 ? SCALER5_DISPCTRL_DSPEISLUR(channel) : in vc4_hvs_unmask_underrun()
711 atomic_inc(&vc4->underrun); in vc4_hvs_report_underrun()
712 DRM_DEV_ERROR(dev->dev, "HVS underrun\n"); in vc4_hvs_report_underrun()
719 struct vc4_hvs *hvs = vc4->hvs; in vc4_hvs_irq_handler() local
741 dspeislur = vc4->is_vc5 ? SCALER5_DISPCTRL_DSPEISLUR(channel) : in vc4_hvs_irq_handler()
746 vc4_hvs_mask_underrun(hvs, channel); in vc4_hvs_irq_handler()
753 /* Clear every per-channel interrupt flag. */ in vc4_hvs_irq_handler()
763 struct drm_device *drm = minor->dev; in vc4_hvs_debugfs_init()
765 struct vc4_hvs *hvs = vc4->hvs; in vc4_hvs_debugfs_init() local
767 if (!vc4->hvs) in vc4_hvs_debugfs_init()
768 return -ENODEV; in vc4_hvs_debugfs_init()
770 if (!vc4->is_vc5) in vc4_hvs_debugfs_init()
772 minor->debugfs_root, in vc4_hvs_debugfs_init()
773 &vc4->load_tracker_enabled); in vc4_hvs_debugfs_init()
779 vc4_debugfs_add_regset32(drm, "hvs_regs", &hvs->regset); in vc4_hvs_debugfs_init()
786 struct drm_device *drm = &vc4->base; in __vc4_hvs_alloc()
787 struct vc4_hvs *hvs; in __vc4_hvs_alloc() local
789 hvs = drmm_kzalloc(drm, sizeof(*hvs), GFP_KERNEL); in __vc4_hvs_alloc()
790 if (!hvs) in __vc4_hvs_alloc()
791 return ERR_PTR(-ENOMEM); in __vc4_hvs_alloc()
793 hvs->vc4 = vc4; in __vc4_hvs_alloc()
794 hvs->pdev = pdev; in __vc4_hvs_alloc()
796 spin_lock_init(&hvs->mm_lock); in __vc4_hvs_alloc()
798 /* Set up the HVS display list memory manager. We never in __vc4_hvs_alloc()
803 drm_mm_init(&hvs->dlist_mm, in __vc4_hvs_alloc()
805 (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END); in __vc4_hvs_alloc()
807 /* Set up the HVS LBM memory manager. We could have some more in __vc4_hvs_alloc()
812 if (!vc4->is_vc5) in __vc4_hvs_alloc()
813 /* 48k words of 2x12-bit pixels */ in __vc4_hvs_alloc()
814 drm_mm_init(&hvs->lbm_mm, 0, 48 * 1024); in __vc4_hvs_alloc()
816 /* 60k words of 4x12-bit pixels */ in __vc4_hvs_alloc()
817 drm_mm_init(&hvs->lbm_mm, 0, 60 * 1024); in __vc4_hvs_alloc()
819 vc4->hvs = hvs; in __vc4_hvs_alloc()
821 return hvs; in __vc4_hvs_alloc()
829 struct vc4_hvs *hvs = NULL; in vc4_hvs_bind() local
834 hvs = __vc4_hvs_alloc(vc4, NULL); in vc4_hvs_bind()
835 if (IS_ERR(hvs)) in vc4_hvs_bind()
836 return PTR_ERR(hvs); in vc4_hvs_bind()
838 hvs->regs = vc4_ioremap_regs(pdev, 0); in vc4_hvs_bind()
839 if (IS_ERR(hvs->regs)) in vc4_hvs_bind()
840 return PTR_ERR(hvs->regs); in vc4_hvs_bind()
842 hvs->regset.base = hvs->regs; in vc4_hvs_bind()
843 hvs->regset.regs = hvs_regs; in vc4_hvs_bind()
844 hvs->regset.nregs = ARRAY_SIZE(hvs_regs); in vc4_hvs_bind()
846 if (vc4->is_vc5) { in vc4_hvs_bind()
853 return -EINVAL; in vc4_hvs_bind()
858 return -EPROBE_DEFER; in vc4_hvs_bind()
860 hvs->core_clk = devm_clk_get(&pdev->dev, NULL); in vc4_hvs_bind()
861 if (IS_ERR(hvs->core_clk)) { in vc4_hvs_bind()
862 dev_err(&pdev->dev, "Couldn't get core clock\n"); in vc4_hvs_bind()
863 return PTR_ERR(hvs->core_clk); in vc4_hvs_bind()
870 hvs->vc5_hdmi_enable_hdmi_20 = true; in vc4_hvs_bind()
873 hvs->vc5_hdmi_enable_4096by2160 = true; in vc4_hvs_bind()
875 hvs->max_core_rate = max_rate; in vc4_hvs_bind()
877 ret = clk_prepare_enable(hvs->core_clk); in vc4_hvs_bind()
879 dev_err(&pdev->dev, "Couldn't enable the core clock\n"); in vc4_hvs_bind()
884 if (!vc4->is_vc5) in vc4_hvs_bind()
885 hvs->dlist = hvs->regs + SCALER_DLIST_START; in vc4_hvs_bind()
887 hvs->dlist = hvs->regs + SCALER5_DLIST_START; in vc4_hvs_bind()
892 ret = vc4_hvs_upload_linear_kernel(hvs, in vc4_hvs_bind()
893 &hvs->mitchell_netravali_filter, in vc4_hvs_bind()
925 if (!vc4->is_vc5) in vc4_hvs_bind()
969 if (!vc4->is_vc5) { in vc4_hvs_bind()
983 reg |= (top - 1) << 16; in vc4_hvs_bind()
987 reg |= (top - 1) << 16; in vc4_hvs_bind()
991 reg |= (top - 1) << 16; in vc4_hvs_bind()
1022 vc4_hvs_irq_handler, 0, "vc4 hvs", drm); in vc4_hvs_bind()
1034 struct vc4_hvs *hvs = vc4->hvs; in vc4_hvs_unbind() local
1037 if (drm_mm_node_allocated(&vc4->hvs->mitchell_netravali_filter)) in vc4_hvs_unbind()
1038 drm_mm_remove_node(&vc4->hvs->mitchell_netravali_filter); in vc4_hvs_unbind()
1040 drm_mm_for_each_node_safe(node, next, &vc4->hvs->dlist_mm) in vc4_hvs_unbind()
1043 drm_mm_takedown(&vc4->hvs->dlist_mm); in vc4_hvs_unbind()
1045 drm_mm_for_each_node_safe(node, next, &vc4->hvs->lbm_mm) in vc4_hvs_unbind()
1047 drm_mm_takedown(&vc4->hvs->lbm_mm); in vc4_hvs_unbind()
1049 clk_disable_unprepare(hvs->core_clk); in vc4_hvs_unbind()
1051 vc4->hvs = NULL; in vc4_hvs_unbind()
1061 return component_add(&pdev->dev, &vc4_hvs_ops); in vc4_hvs_dev_probe()
1066 component_del(&pdev->dev, &vc4_hvs_ops); in vc4_hvs_dev_remove()
1070 { .compatible = "brcm,bcm2711-hvs" },
1071 { .compatible = "brcm,bcm2835-hvs" },