From 376e5f5449d11811a8e3f61a4d4b54d68c0fd849 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Fri, 27 Jan 2017 15:54:44 -0200 Subject: [PATCH 01/47] MAINTAINERS: add dma-fence* files to Sync File maintainership As Sync File is highly dependent on dma-fence* tracks it under SYNC FILE_FRAMEWORK as well. Signed-off-by: Gustavo Padovan Acked-by: Daniel Vetter Acked-by: Sumit Semwal Link: http://patchwork.freedesktop.org/patch/msgid/20170127175444.7995-1-gustavo@padovan.org --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index bdc4843d4dc5..c1c000d4b644 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3976,6 +3976,7 @@ S: Maintained L: linux-media@vger.kernel.org L: dri-devel@lists.freedesktop.org F: drivers/dma-buf/sync_* +F: drivers/dma-buf/dma-fence* F: drivers/dma-buf/sw_sync.c F: include/linux/sync_file.h F: include/uapi/linux/sync_file.h From ff2ac58a45914cb65ae4bdc716d108193a867038 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 24 Jan 2017 14:12:29 +0100 Subject: [PATCH 02/47] virtio-gpu: disable VIRGL with BE kernel VIRTIO_GPU_F_VIRGL is added in features list only for LE kernel, so we must check for it only on LE kernel, otherwise virtio_has_feature() calls BUG() and crashes the kernel. Suggested-by: Gerd Hoffmann Signed-off-by: Laurent Vivier Link: http://patchwork.freedesktop.org/patch/msgid/1485263549-28743-1-git-send-email-lvivier@redhat.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_kms.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index fae75394b5d0..30f989a0cafc 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -166,10 +166,14 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) INIT_WORK(&vgdev->config_changed_work, virtio_gpu_config_changed_work_func); +#ifdef __LITTLE_ENDIAN if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_VIRGL)) vgdev->has_virgl_3d = true; DRM_INFO("virgl 3d acceleration %s\n", - vgdev->has_virgl_3d ? "enabled" : "not available"); + vgdev->has_virgl_3d ? "enabled" : "not supported by host"); +#else + DRM_INFO("virgl 3d acceleration not supported by guest\n"); +#endif ret = vgdev->vdev->config->find_vqs(vgdev->vdev, 2, vqs, callbacks, names); From cbef9099398cbc45c4245b413ec73737ed88eb83 Mon Sep 17 00:00:00 2001 From: Dhinakaran Pandiyan Date: Mon, 30 Jan 2017 22:18:38 -0800 Subject: [PATCH 03/47] drm/docs: Fix documentation for drm_atomic_set_mode_for_crtc drm_atomic_set_mode_for_crtc() modifies the .enable member of CRTC state but documentation claims otherwise, fix that. Signed-off-by: Dhinakaran Pandiyan Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1485843518-28438-1-git-send-email-dhinakaran.pandiyan@intel.com --- drivers/gpu/drm/drm_atomic.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index e5b738660d66..c97588a28216 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -307,9 +307,8 @@ static s64 __user *get_out_fence_for_crtc(struct drm_atomic_state *state, * @state: the CRTC whose incoming state to update * @mode: kernel-internal mode to use for the CRTC, or NULL to disable * - * Set a mode (originating from the kernel) on the desired CRTC state. Does - * not change any other state properties, including enable, active, or - * mode_changed. + * Set a mode (originating from the kernel) on the desired CRTC state and update + * the enable property. * * RETURNS: * Zero on success, error code on failure. Cannot return -EDEADLK. From 8f2e045ec878ef77aa2b2650f40fdb7d01b831af Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 27 Jan 2017 17:34:51 +0200 Subject: [PATCH 04/47] drm/color: un-inline drm_color_lut_extract() The function is not that big, but it's also not used for anything performance critical. Make it a normal function. As a side effect, this apparently makes sparse smarter about what it's doing, and gets rid of the warning: ./include/drm/drm_color_mgmt.h:53:28: warning: shift too big (4294967295) for type unsigned long ./include/drm/drm_color_mgmt.h:53:28: warning: cast truncates bits from constant value (8000000000000000 becomes 0) v2: rebased Cc: Lionel Landwerlin Reviewed-by: Daniel Vetter Reviewed-by: Lionel Landwerlin Signed-off-by: Jani Nikula Link: http://patchwork.freedesktop.org/patch/msgid/1485531291-24821-1-git-send-email-jani.nikula@intel.com --- drivers/gpu/drm/drm_color_mgmt.c | 24 ++++++++++++++++++++++++ include/drm/drm_color_mgmt.h | 27 ++------------------------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index 789b4c65cd69..cc23b9a505c0 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -87,6 +87,30 @@ * "GAMMA_LUT" property above. */ +/** + * drm_color_lut_extract - clamp and round LUT entries + * @user_input: input value + * @bit_precision: number of bits the hw LUT supports + * + * Extract a degamma/gamma LUT value provided by user (in the form of + * &drm_color_lut entries) and round it to the precision supported by the + * hardware. + */ +uint32_t drm_color_lut_extract(uint32_t user_input, uint32_t bit_precision) +{ + uint32_t val = user_input; + uint32_t max = 0xffff >> (16 - bit_precision); + + /* Round only if we're not using full precision. */ + if (bit_precision < 16) { + val += 1UL << (16 - bit_precision - 1); + val >>= 16 - bit_precision; + } + + return clamp_val(val, 0, max); +} +EXPORT_SYMBOL(drm_color_lut_extract); + /** * drm_crtc_enable_color_mgmt - enable color management properties * @crtc: DRM CRTC diff --git a/include/drm/drm_color_mgmt.h b/include/drm/drm_color_mgmt.h index d9c2f680f5ae..bce4a532836d 100644 --- a/include/drm/drm_color_mgmt.h +++ b/include/drm/drm_color_mgmt.h @@ -25,6 +25,8 @@ #include +uint32_t drm_color_lut_extract(uint32_t user_input, uint32_t bit_precision); + void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc, uint degamma_lut_size, bool has_ctm, @@ -33,29 +35,4 @@ void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc, int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, int gamma_size); -/** - * drm_color_lut_extract - clamp and round LUT entries - * @user_input: input value - * @bit_precision: number of bits the hw LUT supports - * - * Extract a degamma/gamma LUT value provided by user (in the form of - * &drm_color_lut entries) and round it to the precision supported by the - * hardware. - */ -static inline uint32_t drm_color_lut_extract(uint32_t user_input, - uint32_t bit_precision) -{ - uint32_t val = user_input; - uint32_t max = 0xffff >> (16 - bit_precision); - - /* Round only if we're not using full precision. */ - if (bit_precision < 16) { - val += 1UL << (16 - bit_precision - 1); - val >>= 16 - bit_precision; - } - - return clamp_val(val, 0, max); -} - - #endif From 059c7a5a748d4e7481d8b1b4cf0e182cb81496ad Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Tue, 31 Jan 2017 17:41:09 +0000 Subject: [PATCH 05/47] Documentation/gpu: drm-mm.rst: fix formatting for struct vm_operations_struct drm-mm.rst contains some unformatted dump of the vm_operations_struct structure. Add some C formatting around it and some context for the dump. While there, update the structure to resemble the new signature for the fault handler after commit 25d3db7600b8 (mm, fs: reduce fault, page_mkwrite, and pfn_mkwrite to take only vmf). Signed-off-by: Liviu Dudau Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170131174109.13690-1-Liviu.Dudau@arm.com --- Documentation/gpu/drm-mm.rst | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index 91d82f39fbf4..f5760b140f13 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst @@ -291,10 +291,17 @@ To use :c:func:`drm_gem_mmap()`, drivers must fill the struct :c:type:`struct drm_driver ` gem_vm_ops field with a pointer to VM operations. -struct vm_operations_struct \*gem_vm_ops struct -vm_operations_struct { void (\*open)(struct vm_area_struct \* area); -void (\*close)(struct vm_area_struct \* area); int (\*fault)(struct -vm_area_struct \*vma, struct vm_fault \*vmf); }; +The VM operations is a :c:type:`struct vm_operations_struct ` +made up of several fields, the more interesting ones being: + +.. code-block:: c + + struct vm_operations_struct { + void (*open)(struct vm_area_struct * area); + void (*close)(struct vm_area_struct * area); + int (*fault)(struct vm_fault *vmf); + }; + The open and close operations must update the GEM object reference count. Drivers can use the :c:func:`drm_gem_vm_open()` and From 969218fee101af5335314e8521fb1a29e49e560b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 17 Jan 2017 17:43:29 +0100 Subject: [PATCH 06/47] drm: Clean up the 1366x768 fixup codes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is just a cleanup, no functional change. The fixup code for 1366x768 in drm_mode_create_from_cmdline_mode() is basically a copy of the existing code in drm_edid.c. Make the latter code public so that it can be called from the former function. Signed-off-by: Takashi Iwai Link: http://patchwork.freedesktop.org/patch/msgid/20170117164329.10551-1-tiwai@suse.de Reviewed-by: Ville Syrjälä [vsyrjala: include drm_crtc_internal.h to make sparse happy] Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/drm_crtc_internal.h | 3 +++ drivers/gpu/drm/drm_edid.c | 8 +++++--- drivers/gpu/drm/drm_modes.c | 8 ++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 1bdcfd566695..955c5690bf64 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -207,3 +207,6 @@ int drm_mode_cursor2_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); + +/* drm_edid.c */ +void drm_mode_fixup_1366x768(struct drm_display_mode *mode); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index baa6ccb3e18b..c8baab9bee0d 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -38,6 +38,8 @@ #include #include +#include "drm_crtc_internal.h" + #define version_greater(edid, maj, min) \ (((edid)->version > (maj)) || \ ((edid)->version == (maj) && (edid)->revision > (min))) @@ -2153,7 +2155,7 @@ drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid, /* fix up 1366x768 mode from 1368x768; * GFT/CVT can't express 1366 width which isn't dividable by 8 */ -static void fixup_mode_1366x768(struct drm_display_mode *mode) +void drm_mode_fixup_1366x768(struct drm_display_mode *mode) { if (mode->hdisplay == 1368 && mode->vdisplay == 768) { mode->hdisplay = 1366; @@ -2177,7 +2179,7 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, if (!newmode) return modes; - fixup_mode_1366x768(newmode); + drm_mode_fixup_1366x768(newmode); if (!mode_in_range(newmode, edid, timing) || !valid_inferred_mode(connector, newmode)) { drm_mode_destroy(dev, newmode); @@ -2206,7 +2208,7 @@ drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, if (!newmode) return modes; - fixup_mode_1366x768(newmode); + drm_mode_fixup_1366x768(newmode); if (!mode_in_range(newmode, edid, timing) || !valid_inferred_mode(connector, newmode)) { drm_mode_destroy(dev, newmode); diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index a8616b1a8d22..fd22c1c891bf 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1481,12 +1481,8 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev, mode->type |= DRM_MODE_TYPE_USERDEF; /* fix up 1368x768: GFT/CVT can't express 1366 width due to alignment */ - if (cmd->xres == 1366 && mode->hdisplay == 1368) { - mode->hdisplay = 1366; - mode->hsync_start--; - mode->hsync_end--; - drm_mode_set_name(mode); - } + if (cmd->xres == 1366) + drm_mode_fixup_1366x768(mode); drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); return mode; } From d17a1bb9b801f7046af29c31b946a9619bfe382d Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 14 Dec 2016 11:46:14 -0800 Subject: [PATCH 07/47] drm/vc4: Set up SCALER_DISPCTRL at boot. We want the HVS on, obviously, and we also want DSP3 (PV1's source) to be muxed from HVS channel 2 like we expect in vc4_crtc.c. The firmware wasn't setting the DSP3 mux up when both the LCD and HDMI were disabled. Signed-off-by: Eric Anholt Acked-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20161214194621.16499-5-eric@anholt.net --- drivers/gpu/drm/vc4/vc4_hvs.c | 14 ++++++++++++++ drivers/gpu/drm/vc4/vc4_regs.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 6fbab1c82cb1..fc68b1b4da52 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -170,6 +170,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) struct vc4_dev *vc4 = drm->dev_private; struct vc4_hvs *hvs = NULL; int ret; + u32 dispctrl; hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL); if (!hvs) @@ -211,6 +212,19 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) return ret; vc4->hvs = hvs; + + dispctrl = HVS_READ(SCALER_DISPCTRL); + + dispctrl |= SCALER_DISPCTRL_ENABLE; + + /* Set DSP3 (PV1) to use HVS channel 2, which would otherwise + * be unused. + */ + dispctrl &= ~SCALER_DISPCTRL_DSP3_MUX_MASK; + dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX); + + HVS_WRITE(SCALER_DISPCTRL, dispctrl); + return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 39f6886b2410..b3b297fba709 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -244,6 +244,9 @@ # define SCALER_DISPCTRL_ENABLE BIT(31) # define SCALER_DISPCTRL_DSP2EISLUR BIT(15) # define SCALER_DISPCTRL_DSP1EISLUR BIT(14) +# define SCALER_DISPCTRL_DSP3_MUX_MASK VC4_MASK(19, 18) +# define SCALER_DISPCTRL_DSP3_MUX_SHIFT 18 + /* Enables Display 0 short line and underrun contribution to * SCALER_DISPSTAT_IRQDISP0. Note that short frame contributions are * always enabled. From a86773d120d75569d151b95ac8e0df657d4bfe48 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 14 Dec 2016 11:46:15 -0800 Subject: [PATCH 08/47] drm/vc4: Add support for feeding DSI encoders from the pixel valve. We have to set a different pixel format, which tells the hardware to use the pix_width field that's fed in sideband from the DSI encoder to divide the "pixel" clock. Signed-off-by: Eric Anholt Acked-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20161214194621.16499-6-eric@anholt.net --- drivers/gpu/drm/vc4/vc4_crtc.c | 33 +++++++++++++++++++-------------- drivers/gpu/drm/vc4/vc4_regs.h | 2 ++ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 75b708b36890..63239b5b5092 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -348,38 +348,40 @@ static u32 vc4_get_fifo_full_level(u32 format) } /* - * Returns the clock select bit for the connector attached to the - * CRTC. + * Returns the encoder attached to the CRTC. + * + * VC4 can only scan out to one encoder at a time, while the DRM core + * allows drivers to push pixels to more than one encoder from the + * same CRTC. */ -static int vc4_get_clock_select(struct drm_crtc *crtc) +static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc) { struct drm_connector *connector; drm_for_each_connector(connector, crtc->dev) { if (connector->state->crtc == crtc) { - struct drm_encoder *encoder = connector->encoder; - struct vc4_encoder *vc4_encoder = - to_vc4_encoder(encoder); - - return vc4_encoder->clock_select; + return connector->encoder; } } - return -1; + return NULL; } 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 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); struct drm_crtc_state *state = crtc->state; struct drm_display_mode *mode = &state->adjusted_mode; bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1; - u32 format = PV_CONTROL_FORMAT_24; + 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; - int clock_select = vc4_get_clock_select(crtc); if (debug_dump_regs) { DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc)); @@ -435,17 +437,19 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) */ CRTC_WRITE(PV_V_CONTROL, PV_VCONTROL_CONTINUOUS | + (is_dsi ? PV_VCONTROL_DSI : 0) | PV_VCONTROL_INTERLACE | VC4_SET_FIELD(mode->htotal * pixel_rep / 2, PV_VCONTROL_ODD_DELAY)); CRTC_WRITE(PV_VSYNCD_EVEN, 0); } else { - CRTC_WRITE(PV_V_CONTROL, PV_VCONTROL_CONTINUOUS); + CRTC_WRITE(PV_V_CONTROL, + PV_VCONTROL_CONTINUOUS | + (is_dsi ? PV_VCONTROL_DSI : 0)); } CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep); - CRTC_WRITE(PV_CONTROL, VC4_SET_FIELD(format, PV_CONTROL_FORMAT) | VC4_SET_FIELD(vc4_get_fifo_full_level(format), @@ -454,7 +458,8 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) PV_CONTROL_CLR_AT_START | PV_CONTROL_TRIGGER_UNDERFLOW | PV_CONTROL_WAIT_HSTART | - VC4_SET_FIELD(clock_select, PV_CONTROL_CLK_SELECT) | + VC4_SET_FIELD(vc4_encoder->clock_select, + PV_CONTROL_CLK_SELECT) | PV_CONTROL_FIFO_CLR | PV_CONTROL_EN); diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index b3b297fba709..385405a2df05 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -190,6 +190,8 @@ # define PV_VCONTROL_ODD_DELAY_SHIFT 6 # define PV_VCONTROL_ODD_FIRST BIT(5) # define PV_VCONTROL_INTERLACE BIT(4) +# define PV_VCONTROL_DSI BIT(3) +# define PV_VCONTROL_COMMAND BIT(2) # define PV_VCONTROL_CONTINUOUS BIT(1) # define PV_VCONTROL_VIDEN BIT(0) From 302cee3648ba1a7322b77511de9112e61a1bfff8 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 14 Dec 2016 11:46:16 -0800 Subject: [PATCH 09/47] dt-bindings: Document the VC4 DSI module nodes. These are part of the vc4 display pipeline. Signed-off-by: Eric Anholt Acked-by: Rob Herring Link: http://patchwork.freedesktop.org/patch/msgid/20161214194621.16499-7-eric@anholt.net --- .../bindings/display/brcm,bcm-vc4.txt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt index e2768703ac2b..34c7fddcea39 100644 --- a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt +++ b/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt @@ -56,6 +56,18 @@ Required properties for V3D: - interrupts: The interrupt number See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt +Required properties for DSI: +- compatible: Should be "brcm,bcm2835-dsi0" or "brcm,bcm2835-dsi1" +- reg: Physical base address and length of the DSI block's registers +- interrupts: The interrupt number + See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt +- clocks: a) phy: The DSI PLL clock feeding the DSI analog PHY + b) escape: The DSI ESC clock from CPRMAN + c) pixel: The DSI pixel clock from CPRMAN +- clock-output-names: + The 3 clocks output from the DSI analog PHY: dsi[01]_byte, + dsi[01]_ddr2, and dsi[01]_ddr + [1] Documentation/devicetree/bindings/media/video-interfaces.txt Example: @@ -99,6 +111,29 @@ dpi: dpi@7e208000 { }; }; +dsi1: dsi@7e700000 { + compatible = "brcm,bcm2835-dsi1"; + reg = <0x7e700000 0x8c>; + interrupts = <2 12>; + #address-cells = <1>; + #size-cells = <0>; + #clock-cells = <1>; + + clocks = <&clocks BCM2835_PLLD_DSI1>, + <&clocks BCM2835_CLOCK_DSI1E>, + <&clocks BCM2835_CLOCK_DSI1P>; + clock-names = "phy", "escape", "pixel"; + + clock-output-names = "dsi1_byte", "dsi1_ddr2", "dsi1_ddr"; + + pitouchscreen: panel@0 { + compatible = "raspberrypi,touchscreen"; + reg = <0>; + + <...> + }; +}; + vec: vec@7e806000 { compatible = "brcm,bcm2835-vec"; reg = <0x7e806000 0x1000>; From 4078f57571442345d09edddb2ac79c951b9d6db2 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 31 Jan 2017 11:29:11 -0800 Subject: [PATCH 10/47] drm/vc4: Add DSI driver The DSI0 and DSI1 blocks on the 2835 are related hardware blocks. Some registers move around, and the featureset is slightly different, as DSI1 (the 4-lane DSI) is a later version of the hardware block. This driver doesn't yet enable DSI0, since we don't have any hardware to test against, but it does put a lot of the register definitions and code in place. v2: Use the clk_hw interfaces, don't set CLK_IS_BASIC (from review by Stephen Boyd) Signed-off-by: Eric Anholt Acked-by: Daniel Vetter (v1) Link: http://patchwork.freedesktop.org/patch/msgid/20170131192912.11316-1-eric@anholt.net --- drivers/gpu/drm/vc4/Kconfig | 2 + drivers/gpu/drm/vc4/Makefile | 1 + drivers/gpu/drm/vc4/vc4_debugfs.c | 1 + drivers/gpu/drm/vc4/vc4_drv.c | 1 + drivers/gpu/drm/vc4/vc4_drv.h | 5 + drivers/gpu/drm/vc4/vc4_dsi.c | 1725 +++++++++++++++++++++++++++++ 6 files changed, 1735 insertions(+) create mode 100644 drivers/gpu/drm/vc4/vc4_dsi.c diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig index e53df59cb139..e1517d07cb7d 100644 --- a/drivers/gpu/drm/vc4/Kconfig +++ b/drivers/gpu/drm/vc4/Kconfig @@ -2,10 +2,12 @@ config DRM_VC4 tristate "Broadcom VC4 Graphics" depends on ARCH_BCM2835 || COMPILE_TEST depends on DRM + depends on COMMON_CLK select DRM_KMS_HELPER select DRM_KMS_CMA_HELPER select DRM_GEM_CMA_HELPER select DRM_PANEL + select DRM_MIPI_DSI help Choose this option if you have a system that has a Broadcom VC4 GPU, such as the Raspberry Pi or other BCM2708/BCM2835. diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index 7757f69a8a77..61f45d122bd0 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -8,6 +8,7 @@ vc4-y := \ vc4_crtc.o \ vc4_drv.o \ vc4_dpi.o \ + vc4_dsi.o \ vc4_kms.o \ vc4_gem.o \ vc4_hdmi.o \ diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c index c4d5e6a8d6f2..5db06bdb5f27 100644 --- a/drivers/gpu/drm/vc4/vc4_debugfs.c +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c @@ -18,6 +18,7 @@ static const struct drm_info_list vc4_debugfs_list[] = { {"bo_stats", vc4_bo_stats_debugfs, 0}, {"dpi_regs", vc4_dpi_debugfs_regs, 0}, + {"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}, {"hvs_regs", vc4_hvs_debugfs_regs, 0}, diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index e4f42ebee517..a459745e96f7 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -295,6 +295,7 @@ static struct platform_driver *const component_drivers[] = { &vc4_hdmi_driver, &vc4_vec_driver, &vc4_dpi_driver, + &vc4_dsi_driver, &vc4_hvs_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 78e3e5a43fb0..0e59f3ee1b83 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -18,6 +18,7 @@ struct vc4_dev { struct vc4_hvs *hvs; struct vc4_v3d *v3d; struct vc4_dpi *dpi; + struct vc4_dsi *dsi1; struct vc4_vec *vec; struct drm_fbdev_cma *fbdev; @@ -465,6 +466,10 @@ void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); extern struct platform_driver vc4_dpi_driver; int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused); +/* vc4_dsi.c */ +extern struct platform_driver vc4_dsi_driver; +int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused); + /* vc4_gem.c */ void vc4_gem_init(struct drm_device *dev); void vc4_gem_destroy(struct drm_device *dev); diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c new file mode 100644 index 000000000000..2736b0331beb --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -0,0 +1,1725 @@ +/* + * Copyright (C) 2016 Broadcom + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * DOC: VC4 DSI0/DSI1 module + * + * BCM2835 contains two DSI modules, DSI0 and DSI1. DSI0 is a + * single-lane DSI controller, while DSI1 is a more modern 4-lane DSI + * controller. + * + * Most Raspberry Pi boards expose DSI1 as their "DISPLAY" connector, + * while the compute module brings both DSI0 and DSI1 out. + * + * This driver has been tested for DSI1 video-mode display only + * currently, with most of the information necessary for DSI0 + * hopefully present. + */ + +#include "drm_atomic_helper.h" +#include "drm_crtc_helper.h" +#include "drm_edid.h" +#include "drm_mipi_dsi.h" +#include "drm_panel.h" +#include "linux/clk.h" +#include "linux/clk-provider.h" +#include "linux/completion.h" +#include "linux/component.h" +#include "linux/dmaengine.h" +#include "linux/i2c.h" +#include "linux/of_address.h" +#include "linux/of_platform.h" +#include "linux/pm_runtime.h" +#include "vc4_drv.h" +#include "vc4_regs.h" + +#define DSI_CMD_FIFO_DEPTH 16 +#define DSI_PIX_FIFO_DEPTH 256 +#define DSI_PIX_FIFO_WIDTH 4 + +#define DSI0_CTRL 0x00 + +/* Command packet control. */ +#define DSI0_TXPKT1C 0x04 /* AKA PKTC */ +#define DSI1_TXPKT1C 0x04 +# define DSI_TXPKT1C_TRIG_CMD_MASK VC4_MASK(31, 24) +# define DSI_TXPKT1C_TRIG_CMD_SHIFT 24 +# define DSI_TXPKT1C_CMD_REPEAT_MASK VC4_MASK(23, 10) +# define DSI_TXPKT1C_CMD_REPEAT_SHIFT 10 + +# define DSI_TXPKT1C_DISPLAY_NO_MASK VC4_MASK(9, 8) +# define DSI_TXPKT1C_DISPLAY_NO_SHIFT 8 +/* Short, trigger, BTA, or a long packet that fits all in CMDFIFO. */ +# define DSI_TXPKT1C_DISPLAY_NO_SHORT 0 +/* Primary display where cmdfifo provides part of the payload and + * pixelvalve the rest. + */ +# define DSI_TXPKT1C_DISPLAY_NO_PRIMARY 1 +/* Secondary display where cmdfifo provides part of the payload and + * pixfifo the rest. + */ +# define DSI_TXPKT1C_DISPLAY_NO_SECONDARY 2 + +# define DSI_TXPKT1C_CMD_TX_TIME_MASK VC4_MASK(7, 6) +# define DSI_TXPKT1C_CMD_TX_TIME_SHIFT 6 + +# define DSI_TXPKT1C_CMD_CTRL_MASK VC4_MASK(5, 4) +# define DSI_TXPKT1C_CMD_CTRL_SHIFT 4 +/* Command only. Uses TXPKT1H and DISPLAY_NO */ +# define DSI_TXPKT1C_CMD_CTRL_TX 0 +/* Command with BTA for either ack or read data. */ +# define DSI_TXPKT1C_CMD_CTRL_RX 1 +/* Trigger according to TRIG_CMD */ +# define DSI_TXPKT1C_CMD_CTRL_TRIG 2 +/* BTA alone for getting error status after a command, or a TE trigger + * without a previous command. + */ +# define DSI_TXPKT1C_CMD_CTRL_BTA 3 + +# define DSI_TXPKT1C_CMD_MODE_LP BIT(3) +# define DSI_TXPKT1C_CMD_TYPE_LONG BIT(2) +# define DSI_TXPKT1C_CMD_TE_EN BIT(1) +# define DSI_TXPKT1C_CMD_EN BIT(0) + +/* Command packet header. */ +#define DSI0_TXPKT1H 0x08 /* AKA PKTH */ +#define DSI1_TXPKT1H 0x08 +# define DSI_TXPKT1H_BC_CMDFIFO_MASK VC4_MASK(31, 24) +# define DSI_TXPKT1H_BC_CMDFIFO_SHIFT 24 +# define DSI_TXPKT1H_BC_PARAM_MASK VC4_MASK(23, 8) +# define DSI_TXPKT1H_BC_PARAM_SHIFT 8 +# define DSI_TXPKT1H_BC_DT_MASK VC4_MASK(7, 0) +# define DSI_TXPKT1H_BC_DT_SHIFT 0 + +#define DSI0_RXPKT1H 0x0c /* AKA RX1_PKTH */ +#define DSI1_RXPKT1H 0x14 +# define DSI_RXPKT1H_CRC_ERR BIT(31) +# define DSI_RXPKT1H_DET_ERR BIT(30) +# define DSI_RXPKT1H_ECC_ERR BIT(29) +# define DSI_RXPKT1H_COR_ERR BIT(28) +# define DSI_RXPKT1H_INCOMP_PKT BIT(25) +# define DSI_RXPKT1H_PKT_TYPE_LONG BIT(24) +/* Byte count if DSI_RXPKT1H_PKT_TYPE_LONG */ +# define DSI_RXPKT1H_BC_PARAM_MASK VC4_MASK(23, 8) +# define DSI_RXPKT1H_BC_PARAM_SHIFT 8 +/* Short return bytes if !DSI_RXPKT1H_PKT_TYPE_LONG */ +# define DSI_RXPKT1H_SHORT_1_MASK VC4_MASK(23, 16) +# define DSI_RXPKT1H_SHORT_1_SHIFT 16 +# define DSI_RXPKT1H_SHORT_0_MASK VC4_MASK(15, 8) +# define DSI_RXPKT1H_SHORT_0_SHIFT 8 +# define DSI_RXPKT1H_DT_LP_CMD_MASK VC4_MASK(7, 0) +# define DSI_RXPKT1H_DT_LP_CMD_SHIFT 0 + +#define DSI0_RXPKT2H 0x10 /* AKA RX2_PKTH */ +#define DSI1_RXPKT2H 0x18 +# define DSI_RXPKT1H_DET_ERR BIT(30) +# define DSI_RXPKT1H_ECC_ERR BIT(29) +# define DSI_RXPKT1H_COR_ERR BIT(28) +# define DSI_RXPKT1H_INCOMP_PKT BIT(25) +# define DSI_RXPKT1H_BC_PARAM_MASK VC4_MASK(23, 8) +# define DSI_RXPKT1H_BC_PARAM_SHIFT 8 +# define DSI_RXPKT1H_DT_MASK VC4_MASK(7, 0) +# define DSI_RXPKT1H_DT_SHIFT 0 + +#define DSI0_TXPKT_CMD_FIFO 0x14 /* AKA CMD_DATAF */ +#define DSI1_TXPKT_CMD_FIFO 0x1c + +#define DSI0_DISP0_CTRL 0x18 +# define DSI_DISP0_PIX_CLK_DIV_MASK VC4_MASK(21, 13) +# define DSI_DISP0_PIX_CLK_DIV_SHIFT 13 +# define DSI_DISP0_LP_STOP_CTRL_MASK VC4_MASK(12, 11) +# define DSI_DISP0_LP_STOP_CTRL_SHIFT 11 +# define DSI_DISP0_LP_STOP_DISABLE 0 +# define DSI_DISP0_LP_STOP_PERLINE 1 +# define DSI_DISP0_LP_STOP_PERFRAME 2 + +/* Transmit RGB pixels and null packets only during HACTIVE, instead + * of going to LP-STOP. + */ +# define DSI_DISP_HACTIVE_NULL BIT(10) +/* Transmit blanking packet only during vblank, instead of allowing LP-STOP. */ +# define DSI_DISP_VBLP_CTRL BIT(9) +/* Transmit blanking packet only during HFP, instead of allowing LP-STOP. */ +# define DSI_DISP_HFP_CTRL BIT(8) +/* Transmit blanking packet only during HBP, instead of allowing LP-STOP. */ +# define DSI_DISP_HBP_CTRL BIT(7) +# define DSI_DISP0_CHANNEL_MASK VC4_MASK(6, 5) +# define DSI_DISP0_CHANNEL_SHIFT 5 +/* Enables end events for HSYNC/VSYNC, not just start events. */ +# define DSI_DISP0_ST_END BIT(4) +# define DSI_DISP0_PFORMAT_MASK VC4_MASK(3, 2) +# define DSI_DISP0_PFORMAT_SHIFT 2 +# define DSI_PFORMAT_RGB565 0 +# define DSI_PFORMAT_RGB666_PACKED 1 +# define DSI_PFORMAT_RGB666 2 +# define DSI_PFORMAT_RGB888 3 +/* Default is VIDEO mode. */ +# define DSI_DISP0_COMMAND_MODE BIT(1) +# define DSI_DISP0_ENABLE BIT(0) + +#define DSI0_DISP1_CTRL 0x1c +#define DSI1_DISP1_CTRL 0x2c +/* Format of the data written to TXPKT_PIX_FIFO. */ +# define DSI_DISP1_PFORMAT_MASK VC4_MASK(2, 1) +# define DSI_DISP1_PFORMAT_SHIFT 1 +# define DSI_DISP1_PFORMAT_16BIT 0 +# define DSI_DISP1_PFORMAT_24BIT 1 +# define DSI_DISP1_PFORMAT_32BIT_LE 2 +# define DSI_DISP1_PFORMAT_32BIT_BE 3 + +/* DISP1 is always command mode. */ +# define DSI_DISP1_ENABLE BIT(0) + +#define DSI0_TXPKT_PIX_FIFO 0x20 /* AKA PIX_FIFO */ + +#define DSI0_INT_STAT 0x24 +#define DSI0_INT_EN 0x28 +# define DSI1_INT_PHY_D3_ULPS BIT(30) +# define DSI1_INT_PHY_D3_STOP BIT(29) +# define DSI1_INT_PHY_D2_ULPS BIT(28) +# define DSI1_INT_PHY_D2_STOP BIT(27) +# define DSI1_INT_PHY_D1_ULPS BIT(26) +# define DSI1_INT_PHY_D1_STOP BIT(25) +# define DSI1_INT_PHY_D0_ULPS BIT(24) +# define DSI1_INT_PHY_D0_STOP BIT(23) +# define DSI1_INT_FIFO_ERR BIT(22) +# define DSI1_INT_PHY_DIR_RTF BIT(21) +# define DSI1_INT_PHY_RXLPDT BIT(20) +# define DSI1_INT_PHY_RXTRIG BIT(19) +# define DSI1_INT_PHY_D0_LPDT BIT(18) +# define DSI1_INT_PHY_DIR_FTR BIT(17) + +/* Signaled when the clock lane enters the given state. */ +# define DSI1_INT_PHY_CLOCK_ULPS BIT(16) +# define DSI1_INT_PHY_CLOCK_HS BIT(15) +# define DSI1_INT_PHY_CLOCK_STOP BIT(14) + +/* Signaled on timeouts */ +# define DSI1_INT_PR_TO BIT(13) +# define DSI1_INT_TA_TO BIT(12) +# define DSI1_INT_LPRX_TO BIT(11) +# define DSI1_INT_HSTX_TO BIT(10) + +/* Contention on a line when trying to drive the line low */ +# define DSI1_INT_ERR_CONT_LP1 BIT(9) +# define DSI1_INT_ERR_CONT_LP0 BIT(8) + +/* Control error: incorrect line state sequence on data lane 0. */ +# define DSI1_INT_ERR_CONTROL BIT(7) +/* LPDT synchronization error (bits received not a multiple of 8. */ + +# define DSI1_INT_ERR_SYNC_ESC BIT(6) +/* Signaled after receiving an error packet from the display in + * response to a read. + */ +# define DSI1_INT_RXPKT2 BIT(5) +/* Signaled after receiving a packet. The header and optional short + * response will be in RXPKT1H, and a long response will be in the + * RXPKT_FIFO. + */ +# define DSI1_INT_RXPKT1 BIT(4) +# define DSI1_INT_TXPKT2_DONE BIT(3) +# define DSI1_INT_TXPKT2_END BIT(2) +/* Signaled after all repeats of TXPKT1 are transferred. */ +# define DSI1_INT_TXPKT1_DONE BIT(1) +/* Signaled after each TXPKT1 repeat is scheduled. */ +# define DSI1_INT_TXPKT1_END BIT(0) + +#define DSI1_INTERRUPTS_ALWAYS_ENABLED (DSI1_INT_ERR_SYNC_ESC | \ + DSI1_INT_ERR_CONTROL | \ + DSI1_INT_ERR_CONT_LP0 | \ + DSI1_INT_ERR_CONT_LP1 | \ + DSI1_INT_HSTX_TO | \ + DSI1_INT_LPRX_TO | \ + DSI1_INT_TA_TO | \ + DSI1_INT_PR_TO) + +#define DSI0_STAT 0x2c +#define DSI0_HSTX_TO_CNT 0x30 +#define DSI0_LPRX_TO_CNT 0x34 +#define DSI0_TA_TO_CNT 0x38 +#define DSI0_PR_TO_CNT 0x3c +#define DSI0_PHYC 0x40 +# define DSI1_PHYC_ESC_CLK_LPDT_MASK VC4_MASK(25, 20) +# define DSI1_PHYC_ESC_CLK_LPDT_SHIFT 20 +# define DSI1_PHYC_HS_CLK_CONTINUOUS BIT(18) +# define DSI0_PHYC_ESC_CLK_LPDT_MASK VC4_MASK(17, 12) +# define DSI0_PHYC_ESC_CLK_LPDT_SHIFT 12 +# define DSI1_PHYC_CLANE_ULPS BIT(17) +# define DSI1_PHYC_CLANE_ENABLE BIT(16) +# define DSI_PHYC_DLANE3_ULPS BIT(13) +# define DSI_PHYC_DLANE3_ENABLE BIT(12) +# define DSI0_PHYC_HS_CLK_CONTINUOUS BIT(10) +# define DSI0_PHYC_CLANE_ULPS BIT(9) +# define DSI_PHYC_DLANE2_ULPS BIT(9) +# define DSI0_PHYC_CLANE_ENABLE BIT(8) +# define DSI_PHYC_DLANE2_ENABLE BIT(8) +# define DSI_PHYC_DLANE1_ULPS BIT(5) +# define DSI_PHYC_DLANE1_ENABLE BIT(4) +# define DSI_PHYC_DLANE0_FORCE_STOP BIT(2) +# define DSI_PHYC_DLANE0_ULPS BIT(1) +# define DSI_PHYC_DLANE0_ENABLE BIT(0) + +#define DSI0_HS_CLT0 0x44 +#define DSI0_HS_CLT1 0x48 +#define DSI0_HS_CLT2 0x4c +#define DSI0_HS_DLT3 0x50 +#define DSI0_HS_DLT4 0x54 +#define DSI0_HS_DLT5 0x58 +#define DSI0_HS_DLT6 0x5c +#define DSI0_HS_DLT7 0x60 + +#define DSI0_PHY_AFEC0 0x64 +# define DSI0_PHY_AFEC0_DDR2CLK_EN BIT(26) +# define DSI0_PHY_AFEC0_DDRCLK_EN BIT(25) +# define DSI0_PHY_AFEC0_LATCH_ULPS BIT(24) +# define DSI1_PHY_AFEC0_IDR_DLANE3_MASK VC4_MASK(31, 29) +# define DSI1_PHY_AFEC0_IDR_DLANE3_SHIFT 29 +# define DSI1_PHY_AFEC0_IDR_DLANE2_MASK VC4_MASK(28, 26) +# define DSI1_PHY_AFEC0_IDR_DLANE2_SHIFT 26 +# define DSI1_PHY_AFEC0_IDR_DLANE1_MASK VC4_MASK(27, 23) +# define DSI1_PHY_AFEC0_IDR_DLANE1_SHIFT 23 +# define DSI1_PHY_AFEC0_IDR_DLANE0_MASK VC4_MASK(22, 20) +# define DSI1_PHY_AFEC0_IDR_DLANE0_SHIFT 20 +# define DSI1_PHY_AFEC0_IDR_CLANE_MASK VC4_MASK(19, 17) +# define DSI1_PHY_AFEC0_IDR_CLANE_SHIFT 17 +# define DSI0_PHY_AFEC0_ACTRL_DLANE1_MASK VC4_MASK(23, 20) +# define DSI0_PHY_AFEC0_ACTRL_DLANE1_SHIFT 20 +# define DSI0_PHY_AFEC0_ACTRL_DLANE0_MASK VC4_MASK(19, 16) +# define DSI0_PHY_AFEC0_ACTRL_DLANE0_SHIFT 16 +# define DSI0_PHY_AFEC0_ACTRL_CLANE_MASK VC4_MASK(15, 12) +# define DSI0_PHY_AFEC0_ACTRL_CLANE_SHIFT 12 +# define DSI1_PHY_AFEC0_DDR2CLK_EN BIT(16) +# define DSI1_PHY_AFEC0_DDRCLK_EN BIT(15) +# define DSI1_PHY_AFEC0_LATCH_ULPS BIT(14) +# define DSI1_PHY_AFEC0_RESET BIT(13) +# define DSI1_PHY_AFEC0_PD BIT(12) +# define DSI0_PHY_AFEC0_RESET BIT(11) +# define DSI1_PHY_AFEC0_PD_BG BIT(11) +# define DSI0_PHY_AFEC0_PD BIT(10) +# define DSI1_PHY_AFEC0_PD_DLANE3 BIT(10) +# define DSI0_PHY_AFEC0_PD_BG BIT(9) +# define DSI1_PHY_AFEC0_PD_DLANE2 BIT(9) +# define DSI0_PHY_AFEC0_PD_DLANE1 BIT(8) +# define DSI1_PHY_AFEC0_PD_DLANE1 BIT(8) +# define DSI_PHY_AFEC0_PTATADJ_MASK VC4_MASK(7, 4) +# define DSI_PHY_AFEC0_PTATADJ_SHIFT 4 +# define DSI_PHY_AFEC0_CTATADJ_MASK VC4_MASK(3, 0) +# define DSI_PHY_AFEC0_CTATADJ_SHIFT 0 + +#define DSI0_PHY_AFEC1 0x68 +# define DSI0_PHY_AFEC1_IDR_DLANE1_MASK VC4_MASK(10, 8) +# define DSI0_PHY_AFEC1_IDR_DLANE1_SHIFT 8 +# define DSI0_PHY_AFEC1_IDR_DLANE0_MASK VC4_MASK(6, 4) +# define DSI0_PHY_AFEC1_IDR_DLANE0_SHIFT 4 +# define DSI0_PHY_AFEC1_IDR_CLANE_MASK VC4_MASK(2, 0) +# define DSI0_PHY_AFEC1_IDR_CLANE_SHIFT 0 + +#define DSI0_TST_SEL 0x6c +#define DSI0_TST_MON 0x70 +#define DSI0_ID 0x74 +# define DSI_ID_VALUE 0x00647369 + +#define DSI1_CTRL 0x00 +# define DSI_CTRL_HS_CLKC_MASK VC4_MASK(15, 14) +# define DSI_CTRL_HS_CLKC_SHIFT 14 +# define DSI_CTRL_HS_CLKC_BYTE 0 +# define DSI_CTRL_HS_CLKC_DDR2 1 +# define DSI_CTRL_HS_CLKC_DDR 2 + +# define DSI_CTRL_RX_LPDT_EOT_DISABLE BIT(13) +# define DSI_CTRL_LPDT_EOT_DISABLE BIT(12) +# define DSI_CTRL_HSDT_EOT_DISABLE BIT(11) +# define DSI_CTRL_SOFT_RESET_CFG BIT(10) +# define DSI_CTRL_CAL_BYTE BIT(9) +# define DSI_CTRL_INV_BYTE BIT(8) +# define DSI_CTRL_CLR_LDF BIT(7) +# define DSI0_CTRL_CLR_PBCF BIT(6) +# define DSI1_CTRL_CLR_RXF BIT(6) +# define DSI0_CTRL_CLR_CPBCF BIT(5) +# define DSI1_CTRL_CLR_PDF BIT(5) +# define DSI0_CTRL_CLR_PDF BIT(4) +# define DSI1_CTRL_CLR_CDF BIT(4) +# define DSI0_CTRL_CLR_CDF BIT(3) +# define DSI0_CTRL_CTRL2 BIT(2) +# define DSI1_CTRL_DISABLE_DISP_CRCC BIT(2) +# define DSI0_CTRL_CTRL1 BIT(1) +# define DSI1_CTRL_DISABLE_DISP_ECCC BIT(1) +# define DSI0_CTRL_CTRL0 BIT(0) +# define DSI1_CTRL_EN BIT(0) +# define DSI0_CTRL_RESET_FIFOS (DSI_CTRL_CLR_LDF | \ + DSI0_CTRL_CLR_PBCF | \ + DSI0_CTRL_CLR_CPBCF | \ + DSI0_CTRL_CLR_PDF | \ + DSI0_CTRL_CLR_CDF) +# define DSI1_CTRL_RESET_FIFOS (DSI_CTRL_CLR_LDF | \ + DSI1_CTRL_CLR_RXF | \ + DSI1_CTRL_CLR_PDF | \ + DSI1_CTRL_CLR_CDF) + +#define DSI1_TXPKT2C 0x0c +#define DSI1_TXPKT2H 0x10 +#define DSI1_TXPKT_PIX_FIFO 0x20 +#define DSI1_RXPKT_FIFO 0x24 +#define DSI1_DISP0_CTRL 0x28 +#define DSI1_INT_STAT 0x30 +#define DSI1_INT_EN 0x34 +/* State reporting bits. These mostly behave like INT_STAT, where + * writing a 1 clears the bit. + */ +#define DSI1_STAT 0x38 +# define DSI1_STAT_PHY_D3_ULPS BIT(31) +# define DSI1_STAT_PHY_D3_STOP BIT(30) +# define DSI1_STAT_PHY_D2_ULPS BIT(29) +# define DSI1_STAT_PHY_D2_STOP BIT(28) +# define DSI1_STAT_PHY_D1_ULPS BIT(27) +# define DSI1_STAT_PHY_D1_STOP BIT(26) +# define DSI1_STAT_PHY_D0_ULPS BIT(25) +# define DSI1_STAT_PHY_D0_STOP BIT(24) +# define DSI1_STAT_FIFO_ERR BIT(23) +# define DSI1_STAT_PHY_RXLPDT BIT(22) +# define DSI1_STAT_PHY_RXTRIG BIT(21) +# define DSI1_STAT_PHY_D0_LPDT BIT(20) +/* Set when in forward direction */ +# define DSI1_STAT_PHY_DIR BIT(19) +# define DSI1_STAT_PHY_CLOCK_ULPS BIT(18) +# define DSI1_STAT_PHY_CLOCK_HS BIT(17) +# define DSI1_STAT_PHY_CLOCK_STOP BIT(16) +# define DSI1_STAT_PR_TO BIT(15) +# define DSI1_STAT_TA_TO BIT(14) +# define DSI1_STAT_LPRX_TO BIT(13) +# define DSI1_STAT_HSTX_TO BIT(12) +# define DSI1_STAT_ERR_CONT_LP1 BIT(11) +# define DSI1_STAT_ERR_CONT_LP0 BIT(10) +# define DSI1_STAT_ERR_CONTROL BIT(9) +# define DSI1_STAT_ERR_SYNC_ESC BIT(8) +# define DSI1_STAT_RXPKT2 BIT(7) +# define DSI1_STAT_RXPKT1 BIT(6) +# define DSI1_STAT_TXPKT2_BUSY BIT(5) +# define DSI1_STAT_TXPKT2_DONE BIT(4) +# define DSI1_STAT_TXPKT2_END BIT(3) +# define DSI1_STAT_TXPKT1_BUSY BIT(2) +# define DSI1_STAT_TXPKT1_DONE BIT(1) +# define DSI1_STAT_TXPKT1_END BIT(0) + +#define DSI1_HSTX_TO_CNT 0x3c +#define DSI1_LPRX_TO_CNT 0x40 +#define DSI1_TA_TO_CNT 0x44 +#define DSI1_PR_TO_CNT 0x48 +#define DSI1_PHYC 0x4c + +#define DSI1_HS_CLT0 0x50 +# define DSI_HS_CLT0_CZERO_MASK VC4_MASK(26, 18) +# define DSI_HS_CLT0_CZERO_SHIFT 18 +# define DSI_HS_CLT0_CPRE_MASK VC4_MASK(17, 9) +# define DSI_HS_CLT0_CPRE_SHIFT 9 +# define DSI_HS_CLT0_CPREP_MASK VC4_MASK(8, 0) +# define DSI_HS_CLT0_CPREP_SHIFT 0 + +#define DSI1_HS_CLT1 0x54 +# define DSI_HS_CLT1_CTRAIL_MASK VC4_MASK(17, 9) +# define DSI_HS_CLT1_CTRAIL_SHIFT 9 +# define DSI_HS_CLT1_CPOST_MASK VC4_MASK(8, 0) +# define DSI_HS_CLT1_CPOST_SHIFT 0 + +#define DSI1_HS_CLT2 0x58 +# define DSI_HS_CLT2_WUP_MASK VC4_MASK(23, 0) +# define DSI_HS_CLT2_WUP_SHIFT 0 + +#define DSI1_HS_DLT3 0x5c +# define DSI_HS_DLT3_EXIT_MASK VC4_MASK(26, 18) +# define DSI_HS_DLT3_EXIT_SHIFT 18 +# define DSI_HS_DLT3_ZERO_MASK VC4_MASK(17, 9) +# define DSI_HS_DLT3_ZERO_SHIFT 9 +# define DSI_HS_DLT3_PRE_MASK VC4_MASK(8, 0) +# define DSI_HS_DLT3_PRE_SHIFT 0 + +#define DSI1_HS_DLT4 0x60 +# define DSI_HS_DLT4_ANLAT_MASK VC4_MASK(22, 18) +# define DSI_HS_DLT4_ANLAT_SHIFT 18 +# define DSI_HS_DLT4_TRAIL_MASK VC4_MASK(17, 9) +# define DSI_HS_DLT4_TRAIL_SHIFT 9 +# define DSI_HS_DLT4_LPX_MASK VC4_MASK(8, 0) +# define DSI_HS_DLT4_LPX_SHIFT 0 + +#define DSI1_HS_DLT5 0x64 +# define DSI_HS_DLT5_INIT_MASK VC4_MASK(23, 0) +# define DSI_HS_DLT5_INIT_SHIFT 0 + +#define DSI1_HS_DLT6 0x68 +# define DSI_HS_DLT6_TA_GET_MASK VC4_MASK(31, 24) +# define DSI_HS_DLT6_TA_GET_SHIFT 24 +# define DSI_HS_DLT6_TA_SURE_MASK VC4_MASK(23, 16) +# define DSI_HS_DLT6_TA_SURE_SHIFT 16 +# define DSI_HS_DLT6_TA_GO_MASK VC4_MASK(15, 8) +# define DSI_HS_DLT6_TA_GO_SHIFT 8 +# define DSI_HS_DLT6_LP_LPX_MASK VC4_MASK(7, 0) +# define DSI_HS_DLT6_LP_LPX_SHIFT 0 + +#define DSI1_HS_DLT7 0x6c +# define DSI_HS_DLT7_LP_WUP_MASK VC4_MASK(23, 0) +# define DSI_HS_DLT7_LP_WUP_SHIFT 0 + +#define DSI1_PHY_AFEC0 0x70 + +#define DSI1_PHY_AFEC1 0x74 +# define DSI1_PHY_AFEC1_ACTRL_DLANE3_MASK VC4_MASK(19, 16) +# define DSI1_PHY_AFEC1_ACTRL_DLANE3_SHIFT 16 +# define DSI1_PHY_AFEC1_ACTRL_DLANE2_MASK VC4_MASK(15, 12) +# define DSI1_PHY_AFEC1_ACTRL_DLANE2_SHIFT 12 +# define DSI1_PHY_AFEC1_ACTRL_DLANE1_MASK VC4_MASK(11, 8) +# define DSI1_PHY_AFEC1_ACTRL_DLANE1_SHIFT 8 +# define DSI1_PHY_AFEC1_ACTRL_DLANE0_MASK VC4_MASK(7, 4) +# define DSI1_PHY_AFEC1_ACTRL_DLANE0_SHIFT 4 +# define DSI1_PHY_AFEC1_ACTRL_CLANE_MASK VC4_MASK(3, 0) +# define DSI1_PHY_AFEC1_ACTRL_CLANE_SHIFT 0 + +#define DSI1_TST_SEL 0x78 +#define DSI1_TST_MON 0x7c +#define DSI1_PHY_TST1 0x80 +#define DSI1_PHY_TST2 0x84 +#define DSI1_PHY_FIFO_STAT 0x88 +/* Actually, all registers in the range that aren't otherwise claimed + * will return the ID. + */ +#define DSI1_ID 0x8c + +/* General DSI hardware state. */ +struct vc4_dsi { + struct platform_device *pdev; + + struct mipi_dsi_host dsi_host; + struct drm_encoder *encoder; + struct drm_connector *connector; + struct drm_panel *panel; + + void __iomem *regs; + + struct dma_chan *reg_dma_chan; + dma_addr_t reg_dma_paddr; + u32 *reg_dma_mem; + dma_addr_t reg_paddr; + + /* Whether we're on bcm2835's DSI0 or DSI1. */ + int port; + + /* DSI channel for the panel we're connected to. */ + u32 channel; + u32 lanes; + enum mipi_dsi_pixel_format format; + u32 mode_flags; + + /* Input clock from CPRMAN to the digital PHY, for the DSI + * escape clock. + */ + struct clk *escape_clock; + + /* Input clock to the analog PHY, used to generate the DSI bit + * clock. + */ + struct clk *pll_phy_clock; + + /* HS Clocks generated within the DSI analog PHY. */ + struct clk_fixed_factor phy_clocks[3]; + + struct clk_hw_onecell_data *clk_onecell; + + /* Pixel clock output to the pixelvalve, generated from the HS + * clock. + */ + struct clk *pixel_clock; + + struct completion xfer_completion; + int xfer_result; +}; + +#define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host) + +static inline void +dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val) +{ + struct dma_chan *chan = dsi->reg_dma_chan; + struct dma_async_tx_descriptor *tx; + dma_cookie_t cookie; + int ret; + + /* DSI0 should be able to write normally. */ + if (!chan) { + writel(val, dsi->regs + offset); + return; + } + + *dsi->reg_dma_mem = val; + + tx = chan->device->device_prep_dma_memcpy(chan, + dsi->reg_paddr + offset, + dsi->reg_dma_paddr, + 4, 0); + if (!tx) { + DRM_ERROR("Failed to set up DMA register write\n"); + return; + } + + cookie = tx->tx_submit(tx); + ret = dma_submit_error(cookie); + if (ret) { + DRM_ERROR("Failed to submit DMA: %d\n", ret); + return; + } + ret = dma_sync_wait(chan, cookie); + if (ret) + DRM_ERROR("Failed to wait for DMA: %d\n", ret); +} + +#define DSI_READ(offset) readl(dsi->regs + (offset)) +#define DSI_WRITE(offset, val) dsi_dma_workaround_write(dsi, offset, val) +#define DSI_PORT_READ(offset) \ + DSI_READ(dsi->port ? DSI1_##offset : DSI0_##offset) +#define DSI_PORT_WRITE(offset, val) \ + DSI_WRITE(dsi->port ? DSI1_##offset : DSI0_##offset, val) +#define DSI_PORT_BIT(bit) (dsi->port ? DSI1_##bit : DSI0_##bit) + +/* VC4 DSI encoder KMS struct */ +struct vc4_dsi_encoder { + struct vc4_encoder base; + struct vc4_dsi *dsi; +}; + +static inline struct vc4_dsi_encoder * +to_vc4_dsi_encoder(struct drm_encoder *encoder) +{ + return container_of(encoder, struct vc4_dsi_encoder, base.base); +} + +/* VC4 DSI connector KMS struct */ +struct vc4_dsi_connector { + struct drm_connector base; + struct vc4_dsi *dsi; +}; + +static inline struct vc4_dsi_connector * +to_vc4_dsi_connector(struct drm_connector *connector) +{ + return container_of(connector, struct vc4_dsi_connector, base); +} + +#define DSI_REG(reg) { reg, #reg } +static const struct { + u32 reg; + const char *name; +} dsi0_regs[] = { + DSI_REG(DSI0_CTRL), + DSI_REG(DSI0_STAT), + DSI_REG(DSI0_HSTX_TO_CNT), + DSI_REG(DSI0_LPRX_TO_CNT), + DSI_REG(DSI0_TA_TO_CNT), + DSI_REG(DSI0_PR_TO_CNT), + DSI_REG(DSI0_DISP0_CTRL), + DSI_REG(DSI0_DISP1_CTRL), + DSI_REG(DSI0_INT_STAT), + DSI_REG(DSI0_INT_EN), + DSI_REG(DSI0_PHYC), + DSI_REG(DSI0_HS_CLT0), + DSI_REG(DSI0_HS_CLT1), + DSI_REG(DSI0_HS_CLT2), + DSI_REG(DSI0_HS_DLT3), + DSI_REG(DSI0_HS_DLT4), + DSI_REG(DSI0_HS_DLT5), + DSI_REG(DSI0_HS_DLT6), + DSI_REG(DSI0_HS_DLT7), + DSI_REG(DSI0_PHY_AFEC0), + DSI_REG(DSI0_PHY_AFEC1), + DSI_REG(DSI0_ID), +}; + +static const struct { + u32 reg; + const char *name; +} dsi1_regs[] = { + DSI_REG(DSI1_CTRL), + DSI_REG(DSI1_STAT), + DSI_REG(DSI1_HSTX_TO_CNT), + DSI_REG(DSI1_LPRX_TO_CNT), + DSI_REG(DSI1_TA_TO_CNT), + DSI_REG(DSI1_PR_TO_CNT), + DSI_REG(DSI1_DISP0_CTRL), + DSI_REG(DSI1_DISP1_CTRL), + DSI_REG(DSI1_INT_STAT), + DSI_REG(DSI1_INT_EN), + DSI_REG(DSI1_PHYC), + DSI_REG(DSI1_HS_CLT0), + DSI_REG(DSI1_HS_CLT1), + DSI_REG(DSI1_HS_CLT2), + DSI_REG(DSI1_HS_DLT3), + DSI_REG(DSI1_HS_DLT4), + DSI_REG(DSI1_HS_DLT5), + DSI_REG(DSI1_HS_DLT6), + DSI_REG(DSI1_HS_DLT7), + DSI_REG(DSI1_PHY_AFEC0), + DSI_REG(DSI1_PHY_AFEC1), + DSI_REG(DSI1_ID), +}; + +static void vc4_dsi_dump_regs(struct vc4_dsi *dsi) +{ + int i; + + if (dsi->port == 0) { + for (i = 0; i < ARRAY_SIZE(dsi0_regs); i++) { + DRM_INFO("0x%04x (%s): 0x%08x\n", + dsi0_regs[i].reg, dsi0_regs[i].name, + DSI_READ(dsi0_regs[i].reg)); + } + } else { + for (i = 0; i < ARRAY_SIZE(dsi1_regs); i++) { + DRM_INFO("0x%04x (%s): 0x%08x\n", + dsi1_regs[i].reg, dsi1_regs[i].name, + DSI_READ(dsi1_regs[i].reg)); + } + } +} + +#ifdef CONFIG_DEBUG_FS +int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *drm = node->minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + int dsi_index = (uintptr_t)node->info_ent->data; + struct vc4_dsi *dsi = (dsi_index == 1 ? vc4->dsi1 : NULL); + int i; + + if (!dsi) + return 0; + + if (dsi->port == 0) { + for (i = 0; i < ARRAY_SIZE(dsi0_regs); i++) { + seq_printf(m, "0x%04x (%s): 0x%08x\n", + dsi0_regs[i].reg, dsi0_regs[i].name, + DSI_READ(dsi0_regs[i].reg)); + } + } else { + for (i = 0; i < ARRAY_SIZE(dsi1_regs); i++) { + seq_printf(m, "0x%04x (%s): 0x%08x\n", + dsi1_regs[i].reg, dsi1_regs[i].name, + DSI_READ(dsi1_regs[i].reg)); + } + } + + return 0; +} +#endif + +static enum drm_connector_status +vc4_dsi_connector_detect(struct drm_connector *connector, bool force) +{ + struct vc4_dsi_connector *vc4_connector = + to_vc4_dsi_connector(connector); + struct vc4_dsi *dsi = vc4_connector->dsi; + + if (dsi->panel) + return connector_status_connected; + else + return connector_status_disconnected; +} + +static void vc4_dsi_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static int vc4_dsi_connector_get_modes(struct drm_connector *connector) +{ + struct vc4_dsi_connector *vc4_connector = + to_vc4_dsi_connector(connector); + struct vc4_dsi *dsi = vc4_connector->dsi; + + if (dsi->panel) + return drm_panel_get_modes(dsi->panel); + + return 0; +} + +static const struct drm_connector_funcs vc4_dsi_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .detect = vc4_dsi_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = vc4_dsi_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_connector_helper_funcs vc4_dsi_connector_helper_funcs = { + .get_modes = vc4_dsi_connector_get_modes, +}; + +static struct drm_connector *vc4_dsi_connector_init(struct drm_device *dev, + struct vc4_dsi *dsi) +{ + struct drm_connector *connector = NULL; + struct vc4_dsi_connector *dsi_connector; + int ret = 0; + + dsi_connector = devm_kzalloc(dev->dev, sizeof(*dsi_connector), + GFP_KERNEL); + if (!dsi_connector) { + ret = -ENOMEM; + goto fail; + } + connector = &dsi_connector->base; + + dsi_connector->dsi = dsi; + + drm_connector_init(dev, connector, &vc4_dsi_connector_funcs, + DRM_MODE_CONNECTOR_DSI); + drm_connector_helper_add(connector, &vc4_dsi_connector_helper_funcs); + + connector->polled = 0; + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + drm_mode_connector_attach_encoder(connector, dsi->encoder); + + return connector; + +fail: + if (connector) + vc4_dsi_connector_destroy(connector); + + return ERR_PTR(ret); +} + +static void vc4_dsi_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs vc4_dsi_encoder_funcs = { + .destroy = vc4_dsi_encoder_destroy, +}; + +static void vc4_dsi_latch_ulps(struct vc4_dsi *dsi, bool latch) +{ + u32 afec0 = DSI_PORT_READ(PHY_AFEC0); + + if (latch) + afec0 |= DSI_PORT_BIT(PHY_AFEC0_LATCH_ULPS); + else + afec0 &= ~DSI_PORT_BIT(PHY_AFEC0_LATCH_ULPS); + + DSI_PORT_WRITE(PHY_AFEC0, afec0); +} + +/* Enters or exits Ultra Low Power State. */ +static void vc4_dsi_ulps(struct vc4_dsi *dsi, bool ulps) +{ + bool continuous = dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS; + u32 phyc_ulps = ((continuous ? DSI_PORT_BIT(PHYC_CLANE_ULPS) : 0) | + DSI_PHYC_DLANE0_ULPS | + (dsi->lanes > 1 ? DSI_PHYC_DLANE1_ULPS : 0) | + (dsi->lanes > 2 ? DSI_PHYC_DLANE2_ULPS : 0) | + (dsi->lanes > 3 ? DSI_PHYC_DLANE3_ULPS : 0)); + u32 stat_ulps = ((continuous ? DSI1_STAT_PHY_CLOCK_ULPS : 0) | + DSI1_STAT_PHY_D0_ULPS | + (dsi->lanes > 1 ? DSI1_STAT_PHY_D1_ULPS : 0) | + (dsi->lanes > 2 ? DSI1_STAT_PHY_D2_ULPS : 0) | + (dsi->lanes > 3 ? DSI1_STAT_PHY_D3_ULPS : 0)); + u32 stat_stop = ((continuous ? DSI1_STAT_PHY_CLOCK_STOP : 0) | + DSI1_STAT_PHY_D0_STOP | + (dsi->lanes > 1 ? DSI1_STAT_PHY_D1_STOP : 0) | + (dsi->lanes > 2 ? DSI1_STAT_PHY_D2_STOP : 0) | + (dsi->lanes > 3 ? DSI1_STAT_PHY_D3_STOP : 0)); + int ret; + + DSI_PORT_WRITE(STAT, stat_ulps); + DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) | phyc_ulps); + ret = wait_for((DSI_PORT_READ(STAT) & stat_ulps) == stat_ulps, 200); + if (ret) { + dev_warn(&dsi->pdev->dev, + "Timeout waiting for DSI ULPS entry: STAT 0x%08x", + DSI_PORT_READ(STAT)); + DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); + vc4_dsi_latch_ulps(dsi, false); + return; + } + + /* The DSI module can't be disabled while the module is + * generating ULPS state. So, to be able to disable the + * module, we have the AFE latch the ULPS state and continue + * on to having the module enter STOP. + */ + vc4_dsi_latch_ulps(dsi, ulps); + + DSI_PORT_WRITE(STAT, stat_stop); + DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); + ret = wait_for((DSI_PORT_READ(STAT) & stat_stop) == stat_stop, 200); + if (ret) { + dev_warn(&dsi->pdev->dev, + "Timeout waiting for DSI STOP entry: STAT 0x%08x", + DSI_PORT_READ(STAT)); + DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); + return; + } +} + +static u32 +dsi_hs_timing(u32 ui_ns, u32 ns, u32 ui) +{ + /* The HS timings have to be rounded up to a multiple of 8 + * because we're using the byte clock. + */ + return roundup(ui + DIV_ROUND_UP(ns, ui_ns), 8); +} + +/* ESC always runs at 100Mhz. */ +#define ESC_TIME_NS 10 + +static u32 +dsi_esc_timing(u32 ns) +{ + return DIV_ROUND_UP(ns, ESC_TIME_NS); +} + +static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) +{ + struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); + struct vc4_dsi *dsi = vc4_encoder->dsi; + struct device *dev = &dsi->pdev->dev; + + drm_panel_disable(dsi->panel); + + vc4_dsi_ulps(dsi, true); + + drm_panel_unprepare(dsi->panel); + + clk_disable_unprepare(dsi->pll_phy_clock); + clk_disable_unprepare(dsi->escape_clock); + clk_disable_unprepare(dsi->pixel_clock); + + pm_runtime_put(dev); +} + +static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) +{ + struct drm_display_mode *mode = &encoder->crtc->mode; + struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); + struct vc4_dsi *dsi = vc4_encoder->dsi; + struct device *dev = &dsi->pdev->dev; + u32 format = 0, divider = 0; + bool debug_dump_regs = false; + unsigned long hs_clock; + u32 ui_ns; + /* Minimum LP state duration in escape clock cycles. */ + u32 lpx = dsi_esc_timing(60); + unsigned long pixel_clock_hz = mode->clock * 1000; + unsigned long dsip_clock; + unsigned long phy_clock; + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret) { + DRM_ERROR("Failed to runtime PM enable on DSI%d\n", dsi->port); + return; + } + + ret = drm_panel_prepare(dsi->panel); + if (ret) { + DRM_ERROR("Panel failed to prepare\n"); + return; + } + + if (debug_dump_regs) { + DRM_INFO("DSI regs before:\n"); + vc4_dsi_dump_regs(dsi); + } + + switch (dsi->format) { + case MIPI_DSI_FMT_RGB888: + format = DSI_PFORMAT_RGB888; + divider = 24 / dsi->lanes; + break; + case MIPI_DSI_FMT_RGB666: + format = DSI_PFORMAT_RGB666; + divider = 24 / dsi->lanes; + break; + case MIPI_DSI_FMT_RGB666_PACKED: + format = DSI_PFORMAT_RGB666_PACKED; + divider = 18 / dsi->lanes; + break; + case MIPI_DSI_FMT_RGB565: + format = DSI_PFORMAT_RGB565; + divider = 16 / dsi->lanes; + break; + } + + phy_clock = pixel_clock_hz * divider; + ret = clk_set_rate(dsi->pll_phy_clock, phy_clock); + if (ret) { + dev_err(&dsi->pdev->dev, + "Failed to set phy clock to %ld: %d\n", phy_clock, ret); + } + + /* Reset the DSI and all its fifos. */ + DSI_PORT_WRITE(CTRL, + DSI_CTRL_SOFT_RESET_CFG | + DSI_PORT_BIT(CTRL_RESET_FIFOS)); + + DSI_PORT_WRITE(CTRL, + DSI_CTRL_HSDT_EOT_DISABLE | + DSI_CTRL_RX_LPDT_EOT_DISABLE); + + /* Clear all stat bits so we see what has happened during enable. */ + DSI_PORT_WRITE(STAT, DSI_PORT_READ(STAT)); + + /* Set AFE CTR00/CTR1 to release powerdown of analog. */ + if (dsi->port == 0) { + u32 afec0 = (VC4_SET_FIELD(7, DSI_PHY_AFEC0_PTATADJ) | + VC4_SET_FIELD(7, DSI_PHY_AFEC0_CTATADJ)); + + if (dsi->lanes < 2) + afec0 |= DSI0_PHY_AFEC0_PD_DLANE1; + + if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) + afec0 |= DSI0_PHY_AFEC0_RESET; + + DSI_PORT_WRITE(PHY_AFEC0, afec0); + + DSI_PORT_WRITE(PHY_AFEC1, + VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_DLANE1) | + VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_DLANE0) | + VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_CLANE)); + } else { + u32 afec0 = (VC4_SET_FIELD(7, DSI_PHY_AFEC0_PTATADJ) | + VC4_SET_FIELD(7, DSI_PHY_AFEC0_CTATADJ) | + VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_CLANE) | + VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE0) | + VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE1) | + VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE2) | + VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE3)); + + if (dsi->lanes < 4) + afec0 |= DSI1_PHY_AFEC0_PD_DLANE3; + if (dsi->lanes < 3) + afec0 |= DSI1_PHY_AFEC0_PD_DLANE2; + if (dsi->lanes < 2) + afec0 |= DSI1_PHY_AFEC0_PD_DLANE1; + + afec0 |= DSI1_PHY_AFEC0_RESET; + + DSI_PORT_WRITE(PHY_AFEC0, afec0); + + DSI_PORT_WRITE(PHY_AFEC1, 0); + + /* AFEC reset hold time */ + mdelay(1); + } + + ret = clk_prepare_enable(dsi->escape_clock); + if (ret) { + DRM_ERROR("Failed to turn on DSI escape clock: %d\n", ret); + return; + } + + ret = clk_prepare_enable(dsi->pll_phy_clock); + if (ret) { + DRM_ERROR("Failed to turn on DSI PLL: %d\n", ret); + return; + } + + hs_clock = clk_get_rate(dsi->pll_phy_clock); + + /* Yes, we set the DSI0P/DSI1P pixel clock to the byte rate, + * not the pixel clock rate. DSIxP take from the APHY's byte, + * DDR2, or DDR4 clock (we use byte) and feed into the PV at + * that rate. Separately, a value derived from PIX_CLK_DIV + * and HS_CLKC is fed into the PV to divide down to the actual + * pixel clock for pushing pixels into DSI. + */ + dsip_clock = phy_clock / 8; + ret = clk_set_rate(dsi->pixel_clock, dsip_clock); + if (ret) { + dev_err(dev, "Failed to set pixel clock to %ldHz: %d\n", + dsip_clock, ret); + } + + ret = clk_prepare_enable(dsi->pixel_clock); + if (ret) { + DRM_ERROR("Failed to turn on DSI pixel clock: %d\n", ret); + return; + } + + /* How many ns one DSI unit interval is. Note that the clock + * is DDR, so there's an extra divide by 2. + */ + ui_ns = DIV_ROUND_UP(500000000, hs_clock); + + DSI_PORT_WRITE(HS_CLT0, + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 262, 0), + DSI_HS_CLT0_CZERO) | + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 0, 8), + DSI_HS_CLT0_CPRE) | + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 38, 0), + DSI_HS_CLT0_CPREP)); + + DSI_PORT_WRITE(HS_CLT1, + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 60, 0), + DSI_HS_CLT1_CTRAIL) | + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 60, 52), + DSI_HS_CLT1_CPOST)); + + DSI_PORT_WRITE(HS_CLT2, + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 1000000, 0), + DSI_HS_CLT2_WUP)); + + DSI_PORT_WRITE(HS_DLT3, + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 100, 0), + DSI_HS_DLT3_EXIT) | + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 105, 6), + DSI_HS_DLT3_ZERO) | + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 40, 4), + DSI_HS_DLT3_PRE)); + + DSI_PORT_WRITE(HS_DLT4, + VC4_SET_FIELD(dsi_hs_timing(ui_ns, lpx * ESC_TIME_NS, 0), + DSI_HS_DLT4_LPX) | + VC4_SET_FIELD(max(dsi_hs_timing(ui_ns, 0, 8), + dsi_hs_timing(ui_ns, 60, 4)), + DSI_HS_DLT4_TRAIL) | + VC4_SET_FIELD(0, DSI_HS_DLT4_ANLAT)); + + DSI_PORT_WRITE(HS_DLT5, VC4_SET_FIELD(dsi_hs_timing(ui_ns, 1000, 5000), + DSI_HS_DLT5_INIT)); + + DSI_PORT_WRITE(HS_DLT6, + VC4_SET_FIELD(lpx * 5, DSI_HS_DLT6_TA_GET) | + VC4_SET_FIELD(lpx, DSI_HS_DLT6_TA_SURE) | + VC4_SET_FIELD(lpx * 4, DSI_HS_DLT6_TA_GO) | + VC4_SET_FIELD(lpx, DSI_HS_DLT6_LP_LPX)); + + DSI_PORT_WRITE(HS_DLT7, + VC4_SET_FIELD(dsi_esc_timing(1000000), + DSI_HS_DLT7_LP_WUP)); + + DSI_PORT_WRITE(PHYC, + DSI_PHYC_DLANE0_ENABLE | + (dsi->lanes >= 2 ? DSI_PHYC_DLANE1_ENABLE : 0) | + (dsi->lanes >= 3 ? DSI_PHYC_DLANE2_ENABLE : 0) | + (dsi->lanes >= 4 ? DSI_PHYC_DLANE3_ENABLE : 0) | + DSI_PORT_BIT(PHYC_CLANE_ENABLE) | + ((dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? + 0 : DSI_PORT_BIT(PHYC_HS_CLK_CONTINUOUS)) | + (dsi->port == 0 ? + VC4_SET_FIELD(lpx - 1, DSI0_PHYC_ESC_CLK_LPDT) : + VC4_SET_FIELD(lpx - 1, DSI1_PHYC_ESC_CLK_LPDT))); + + DSI_PORT_WRITE(CTRL, + DSI_PORT_READ(CTRL) | + DSI_CTRL_CAL_BYTE); + + /* HS timeout in HS clock cycles: disabled. */ + DSI_PORT_WRITE(HSTX_TO_CNT, 0); + /* LP receive timeout in HS clocks. */ + DSI_PORT_WRITE(LPRX_TO_CNT, 0xffffff); + /* Bus turnaround timeout */ + DSI_PORT_WRITE(TA_TO_CNT, 100000); + /* Display reset sequence timeout */ + DSI_PORT_WRITE(PR_TO_CNT, 100000); + + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { + DSI_PORT_WRITE(DISP0_CTRL, + VC4_SET_FIELD(divider, DSI_DISP0_PIX_CLK_DIV) | + VC4_SET_FIELD(format, DSI_DISP0_PFORMAT) | + VC4_SET_FIELD(DSI_DISP0_LP_STOP_PERFRAME, + DSI_DISP0_LP_STOP_CTRL) | + DSI_DISP0_ST_END | + DSI_DISP0_ENABLE); + } else { + DSI_PORT_WRITE(DISP0_CTRL, + DSI_DISP0_COMMAND_MODE | + DSI_DISP0_ENABLE); + } + + /* Set up DISP1 for transferring long command payloads through + * the pixfifo. + */ + DSI_PORT_WRITE(DISP1_CTRL, + VC4_SET_FIELD(DSI_DISP1_PFORMAT_32BIT_LE, + DSI_DISP1_PFORMAT) | + DSI_DISP1_ENABLE); + + /* Ungate the block. */ + if (dsi->port == 0) + DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI0_CTRL_CTRL0); + else + DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI1_CTRL_EN); + + /* Bring AFE out of reset. */ + if (dsi->port == 0) { + } else { + DSI_PORT_WRITE(PHY_AFEC0, + DSI_PORT_READ(PHY_AFEC0) & + ~DSI1_PHY_AFEC0_RESET); + } + + vc4_dsi_ulps(dsi, false); + + if (debug_dump_regs) { + DRM_INFO("DSI regs after:\n"); + vc4_dsi_dump_regs(dsi); + } + + ret = drm_panel_enable(dsi->panel); + if (ret) { + DRM_ERROR("Panel failed to enable\n"); + drm_panel_unprepare(dsi->panel); + return; + } +} + +static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct vc4_dsi *dsi = host_to_dsi(host); + struct mipi_dsi_packet packet; + u32 pkth = 0, pktc = 0; + int i, ret; + bool is_long = mipi_dsi_packet_format_is_long(msg->type); + u32 cmd_fifo_len = 0, pix_fifo_len = 0; + + mipi_dsi_create_packet(&packet, msg); + + pkth |= VC4_SET_FIELD(packet.header[0], DSI_TXPKT1H_BC_DT); + pkth |= VC4_SET_FIELD(packet.header[1] | + (packet.header[2] << 8), + DSI_TXPKT1H_BC_PARAM); + if (is_long) { + /* Divide data across the various FIFOs we have available. + * The command FIFO takes byte-oriented data, but is of + * limited size. The pixel FIFO (never actually used for + * pixel data in reality) is word oriented, and substantially + * larger. So, we use the pixel FIFO for most of the data, + * sending the residual bytes in the command FIFO at the start. + * + * With this arrangement, the command FIFO will never get full. + */ + if (packet.payload_length <= 16) { + cmd_fifo_len = packet.payload_length; + pix_fifo_len = 0; + } else { + cmd_fifo_len = (packet.payload_length % + DSI_PIX_FIFO_WIDTH); + pix_fifo_len = ((packet.payload_length - cmd_fifo_len) / + DSI_PIX_FIFO_WIDTH); + } + + WARN_ON_ONCE(pix_fifo_len >= DSI_PIX_FIFO_DEPTH); + + pkth |= VC4_SET_FIELD(cmd_fifo_len, DSI_TXPKT1H_BC_CMDFIFO); + } + + if (msg->rx_len) { + pktc |= VC4_SET_FIELD(DSI_TXPKT1C_CMD_CTRL_RX, + DSI_TXPKT1C_CMD_CTRL); + } else { + pktc |= VC4_SET_FIELD(DSI_TXPKT1C_CMD_CTRL_TX, + DSI_TXPKT1C_CMD_CTRL); + } + + for (i = 0; i < cmd_fifo_len; i++) + DSI_PORT_WRITE(TXPKT_CMD_FIFO, packet.payload[i]); + for (i = 0; i < pix_fifo_len; i++) { + const u8 *pix = packet.payload + cmd_fifo_len + i * 4; + + DSI_PORT_WRITE(TXPKT_PIX_FIFO, + pix[0] | + pix[1] << 8 | + pix[2] << 16 | + pix[3] << 24); + } + + if (msg->flags & MIPI_DSI_MSG_USE_LPM) + pktc |= DSI_TXPKT1C_CMD_MODE_LP; + if (is_long) + pktc |= DSI_TXPKT1C_CMD_TYPE_LONG; + + /* Send one copy of the packet. Larger repeats are used for pixel + * data in command mode. + */ + pktc |= VC4_SET_FIELD(1, DSI_TXPKT1C_CMD_REPEAT); + + pktc |= DSI_TXPKT1C_CMD_EN; + if (pix_fifo_len) { + pktc |= VC4_SET_FIELD(DSI_TXPKT1C_DISPLAY_NO_SECONDARY, + DSI_TXPKT1C_DISPLAY_NO); + } else { + pktc |= VC4_SET_FIELD(DSI_TXPKT1C_DISPLAY_NO_SHORT, + DSI_TXPKT1C_DISPLAY_NO); + } + + /* Enable the appropriate interrupt for the transfer completion. */ + dsi->xfer_result = 0; + reinit_completion(&dsi->xfer_completion); + DSI_PORT_WRITE(INT_STAT, DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF); + if (msg->rx_len) { + DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | + DSI1_INT_PHY_DIR_RTF)); + } else { + DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | + DSI1_INT_TXPKT1_DONE)); + } + + /* Send the packet. */ + DSI_PORT_WRITE(TXPKT1H, pkth); + DSI_PORT_WRITE(TXPKT1C, pktc); + + if (!wait_for_completion_timeout(&dsi->xfer_completion, + msecs_to_jiffies(1000))) { + dev_err(&dsi->pdev->dev, "transfer interrupt wait timeout"); + dev_err(&dsi->pdev->dev, "instat: 0x%08x\n", + DSI_PORT_READ(INT_STAT)); + ret = -ETIMEDOUT; + } else { + ret = dsi->xfer_result; + } + + DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); + + if (ret) + goto reset_fifo_and_return; + + if (ret == 0 && msg->rx_len) { + u32 rxpkt1h = DSI_PORT_READ(RXPKT1H); + u8 *msg_rx = msg->rx_buf; + + if (rxpkt1h & DSI_RXPKT1H_PKT_TYPE_LONG) { + u32 rxlen = VC4_GET_FIELD(rxpkt1h, + DSI_RXPKT1H_BC_PARAM); + + if (rxlen != msg->rx_len) { + DRM_ERROR("DSI returned %db, expecting %db\n", + rxlen, (int)msg->rx_len); + ret = -ENXIO; + goto reset_fifo_and_return; + } + + for (i = 0; i < msg->rx_len; i++) + msg_rx[i] = DSI_READ(DSI1_RXPKT_FIFO); + } else { + /* FINISHME: Handle AWER */ + + msg_rx[0] = VC4_GET_FIELD(rxpkt1h, + DSI_RXPKT1H_SHORT_0); + if (msg->rx_len > 1) { + msg_rx[1] = VC4_GET_FIELD(rxpkt1h, + DSI_RXPKT1H_SHORT_1); + } + } + } + + return ret; + +reset_fifo_and_return: + DRM_ERROR("DSI transfer failed, resetting: %d\n", ret); + + DSI_PORT_WRITE(TXPKT1C, DSI_PORT_READ(TXPKT1C) & ~DSI_TXPKT1C_CMD_EN); + udelay(1); + DSI_PORT_WRITE(CTRL, + DSI_PORT_READ(CTRL) | + DSI_PORT_BIT(CTRL_RESET_FIFOS)); + + DSI_PORT_WRITE(TXPKT1C, 0); + DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); + return ret; +} + +static int vc4_dsi_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device) +{ + struct vc4_dsi *dsi = host_to_dsi(host); + int ret = 0; + + dsi->lanes = device->lanes; + dsi->channel = device->channel; + dsi->format = device->format; + dsi->mode_flags = device->mode_flags; + + if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) { + dev_err(&dsi->pdev->dev, + "Only VIDEO mode panels supported currently.\n"); + return 0; + } + + dsi->panel = of_drm_find_panel(device->dev.of_node); + if (!dsi->panel) + return 0; + + ret = drm_panel_attach(dsi->panel, dsi->connector); + if (ret != 0) + return ret; + + drm_helper_hpd_irq_event(dsi->connector->dev); + + return 0; +} + +static int vc4_dsi_host_detach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device) +{ + struct vc4_dsi *dsi = host_to_dsi(host); + + if (dsi->panel) { + int ret = drm_panel_detach(dsi->panel); + + if (ret) + return ret; + + dsi->panel = NULL; + + drm_helper_hpd_irq_event(dsi->connector->dev); + } + + return 0; +} + +static const struct mipi_dsi_host_ops vc4_dsi_host_ops = { + .attach = vc4_dsi_host_attach, + .detach = vc4_dsi_host_detach, + .transfer = vc4_dsi_host_transfer, +}; + +static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = { + .disable = vc4_dsi_encoder_disable, + .enable = vc4_dsi_encoder_enable, +}; + +static const struct of_device_id vc4_dsi_dt_match[] = { + { .compatible = "brcm,bcm2835-dsi1", (void *)(uintptr_t)1 }, + {} +}; + +static void dsi_handle_error(struct vc4_dsi *dsi, + irqreturn_t *ret, u32 stat, u32 bit, + const char *type) +{ + if (!(stat & bit)) + return; + + DRM_ERROR("DSI%d: %s error\n", dsi->port, type); + *ret = IRQ_HANDLED; +} + +static irqreturn_t vc4_dsi_irq_handler(int irq, void *data) +{ + struct vc4_dsi *dsi = data; + u32 stat = DSI_PORT_READ(INT_STAT); + irqreturn_t ret = IRQ_NONE; + + DSI_PORT_WRITE(INT_STAT, stat); + + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_ERR_SYNC_ESC, "LPDT sync"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_ERR_CONTROL, "data lane 0 sequence"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_ERR_CONT_LP0, "LP0 contention"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_ERR_CONT_LP1, "LP1 contention"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_HSTX_TO, "HSTX timeout"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_LPRX_TO, "LPRX timeout"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_TA_TO, "turnaround timeout"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_PR_TO, "peripheral reset timeout"); + + if (stat & (DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF)) { + complete(&dsi->xfer_completion); + ret = IRQ_HANDLED; + } else if (stat & DSI1_INT_HSTX_TO) { + complete(&dsi->xfer_completion); + dsi->xfer_result = -ETIMEDOUT; + ret = IRQ_HANDLED; + } + + return ret; +} + +/** + * Exposes clocks generated by the analog PHY that are consumed by + * CPRMAN (clk-bcm2835.c). + */ +static int +vc4_dsi_init_phy_clocks(struct vc4_dsi *dsi) +{ + struct device *dev = &dsi->pdev->dev; + const char *parent_name = __clk_get_name(dsi->pll_phy_clock); + static const struct { + const char *dsi0_name, *dsi1_name; + int div; + } phy_clocks[] = { + { "dsi0_byte", "dsi1_byte", 8 }, + { "dsi0_ddr2", "dsi1_ddr2", 4 }, + { "dsi0_ddr", "dsi1_ddr", 2 }, + }; + int i; + + dsi->clk_onecell = devm_kzalloc(dev, + sizeof(*dsi->clk_onecell) + + ARRAY_SIZE(phy_clocks) * + sizeof(struct clk_hw *), + GFP_KERNEL); + if (!dsi->clk_onecell) + return -ENOMEM; + dsi->clk_onecell->num = ARRAY_SIZE(phy_clocks); + + for (i = 0; i < ARRAY_SIZE(phy_clocks); i++) { + struct clk_fixed_factor *fix = &dsi->phy_clocks[i]; + struct clk_init_data init; + int ret; + + /* We just use core fixed factor clock ops for the PHY + * clocks. The clocks are actually gated by the + * PHY_AFEC0_DDRCLK_EN bits, which we should be + * setting if we use the DDR/DDR2 clocks. However, + * vc4_dsi_encoder_enable() is setting up both AFEC0, + * setting both our parent DSI PLL's rate and this + * clock's rate, so it knows if DDR/DDR2 are going to + * be used and could enable the gates itself. + */ + fix->mult = 1; + fix->div = phy_clocks[i].div; + fix->hw.init = &init; + + memset(&init, 0, sizeof(init)); + init.parent_names = &parent_name; + init.num_parents = 1; + if (dsi->port == 1) + init.name = phy_clocks[i].dsi1_name; + else + init.name = phy_clocks[i].dsi0_name; + init.ops = &clk_fixed_factor_ops; + + ret = devm_clk_hw_register(dev, &fix->hw); + if (ret) + return ret; + + dsi->clk_onecell->hws[i] = &fix->hw; + } + + return of_clk_add_hw_provider(dev->of_node, + of_clk_hw_onecell_get, + dsi->clk_onecell); +} + +static int vc4_dsi_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_dsi *dsi; + struct vc4_dsi_encoder *vc4_dsi_encoder; + const struct of_device_id *match; + dma_cap_mask_t dma_mask; + int ret; + + dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; + + match = of_match_device(vc4_dsi_dt_match, dev); + if (!match) + return -ENODEV; + + dsi->port = (uintptr_t)match->data; + + vc4_dsi_encoder = devm_kzalloc(dev, sizeof(*vc4_dsi_encoder), + GFP_KERNEL); + if (!vc4_dsi_encoder) + return -ENOMEM; + vc4_dsi_encoder->base.type = VC4_ENCODER_TYPE_DSI1; + vc4_dsi_encoder->dsi = dsi; + dsi->encoder = &vc4_dsi_encoder->base.base; + + dsi->pdev = pdev; + dsi->regs = vc4_ioremap_regs(pdev, 0); + if (IS_ERR(dsi->regs)) + return PTR_ERR(dsi->regs); + + if (DSI_PORT_READ(ID) != DSI_ID_VALUE) { + dev_err(dev, "Port returned 0x%08x for ID instead of 0x%08x\n", + DSI_PORT_READ(ID), DSI_ID_VALUE); + return -ENODEV; + } + + /* DSI1 has a broken AXI slave that doesn't respond to writes + * from the ARM. It does handle writes from the DMA engine, + * so set up a channel for talking to it. + */ + if (dsi->port == 1) { + dsi->reg_dma_mem = dma_alloc_coherent(dev, 4, + &dsi->reg_dma_paddr, + GFP_KERNEL); + if (!dsi->reg_dma_mem) { + DRM_ERROR("Failed to get DMA memory\n"); + return -ENOMEM; + } + + dma_cap_zero(dma_mask); + dma_cap_set(DMA_MEMCPY, dma_mask); + dsi->reg_dma_chan = dma_request_chan_by_mask(&dma_mask); + if (IS_ERR(dsi->reg_dma_chan)) { + ret = PTR_ERR(dsi->reg_dma_chan); + if (ret != -EPROBE_DEFER) + DRM_ERROR("Failed to get DMA channel: %d\n", + ret); + return ret; + } + + /* Get the physical address of the device's registers. The + * struct resource for the regs gives us the bus address + * instead. + */ + dsi->reg_paddr = be32_to_cpup(of_get_address(dev->of_node, + 0, NULL, NULL)); + } + + init_completion(&dsi->xfer_completion); + /* At startup enable error-reporting interrupts and nothing else. */ + DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); + /* Clear any existing interrupt state. */ + DSI_PORT_WRITE(INT_STAT, DSI_PORT_READ(INT_STAT)); + + ret = devm_request_irq(dev, platform_get_irq(pdev, 0), + vc4_dsi_irq_handler, 0, "vc4 dsi", dsi); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get interrupt: %d\n", ret); + return ret; + } + + dsi->escape_clock = devm_clk_get(dev, "escape"); + if (IS_ERR(dsi->escape_clock)) { + ret = PTR_ERR(dsi->escape_clock); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get escape clock: %d\n", ret); + return ret; + } + + dsi->pll_phy_clock = devm_clk_get(dev, "phy"); + if (IS_ERR(dsi->pll_phy_clock)) { + ret = PTR_ERR(dsi->pll_phy_clock); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get phy clock: %d\n", ret); + return ret; + } + + dsi->pixel_clock = devm_clk_get(dev, "pixel"); + if (IS_ERR(dsi->pixel_clock)) { + ret = PTR_ERR(dsi->pixel_clock); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get pixel clock: %d\n", ret); + return ret; + } + + /* The esc clock rate is supposed to always be 100Mhz. */ + ret = clk_set_rate(dsi->escape_clock, 100 * 1000000); + if (ret) { + dev_err(dev, "Failed to set esc clock: %d\n", ret); + return ret; + } + + ret = vc4_dsi_init_phy_clocks(dsi); + if (ret) + return ret; + + if (dsi->port == 1) + vc4->dsi1 = dsi; + + drm_encoder_init(drm, dsi->encoder, &vc4_dsi_encoder_funcs, + DRM_MODE_ENCODER_DSI, NULL); + drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs); + + dsi->connector = vc4_dsi_connector_init(drm, dsi); + if (IS_ERR(dsi->connector)) { + ret = PTR_ERR(dsi->connector); + goto err_destroy_encoder; + } + + dsi->dsi_host.ops = &vc4_dsi_host_ops; + dsi->dsi_host.dev = dev; + + mipi_dsi_host_register(&dsi->dsi_host); + + dev_set_drvdata(dev, dsi); + + pm_runtime_enable(dev); + + return 0; + +err_destroy_encoder: + vc4_dsi_encoder_destroy(dsi->encoder); + + return ret; +} + +static void vc4_dsi_unbind(struct device *dev, struct device *master, + void *data) +{ + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_dsi *dsi = dev_get_drvdata(dev); + + pm_runtime_disable(dev); + + vc4_dsi_connector_destroy(dsi->connector); + vc4_dsi_encoder_destroy(dsi->encoder); + + mipi_dsi_host_unregister(&dsi->dsi_host); + + clk_disable_unprepare(dsi->pll_phy_clock); + clk_disable_unprepare(dsi->escape_clock); + + if (dsi->port == 1) + vc4->dsi1 = NULL; +} + +static const struct component_ops vc4_dsi_ops = { + .bind = vc4_dsi_bind, + .unbind = vc4_dsi_unbind, +}; + +static int vc4_dsi_dev_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &vc4_dsi_ops); +} + +static int vc4_dsi_dev_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vc4_dsi_ops); + return 0; +} + +struct platform_driver vc4_dsi_driver = { + .probe = vc4_dsi_dev_probe, + .remove = vc4_dsi_dev_remove, + .driver = { + .name = "vc4_dsi", + .of_match_table = vc4_dsi_dt_match, + }, +}; From 1acf566195e135bc0b557ecb8ad5395b47d9ad27 Mon Sep 17 00:00:00 2001 From: Max Staudt Date: Wed, 18 Jan 2017 19:10:12 +0100 Subject: [PATCH 11/47] drm/bochs: Implement nomodeset Up until now, the bochsdrm driver didn't handle the nomodeset option at boot, and didn't provide a "modeset" module option either. This patch implements both. The new parameter can be used by specifying bochs-drm.modeset=0 at boot time. Signed-off-by: Max Staudt Link: http://patchwork.freedesktop.org/patch/msgid/1484763012-17783-1-git-send-email-mstaudt@suse.de Cc: Gerd Hoffmann Cc: David Airlie Cc: Daniel Vetter Cc: dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/bochs/bochs_drv.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c index 8a12b3f6fc66..aa342515ddf4 100644 --- a/drivers/gpu/drm/bochs/bochs_drv.c +++ b/drivers/gpu/drm/bochs/bochs_drv.c @@ -12,6 +12,10 @@ #include "bochs.h" +static int bochs_modeset = -1; +module_param_named(modeset, bochs_modeset, int, 0444); +MODULE_PARM_DESC(modeset, "enable/disable kernel modesetting"); + static bool enable_fbdev = true; module_param_named(fbdev, enable_fbdev, bool, 0444); MODULE_PARM_DESC(fbdev, "register fbdev device"); @@ -214,6 +218,12 @@ static struct pci_driver bochs_pci_driver = { static int __init bochs_init(void) { + if (vgacon_text_force() && bochs_modeset == -1) + return -EINVAL; + + if (bochs_modeset == 0) + return -EINVAL; + return drm_pci_init(&bochs_driver, &bochs_pci_driver); } From 768851b92cf224a6dc37cb99d78dd94377d51f0e Mon Sep 17 00:00:00 2001 From: Gabriel Krisman Bertazi Date: Thu, 26 Jan 2017 23:05:45 -0200 Subject: [PATCH 12/47] drm: qxl: Drop misleading comment No longer true since commit 07f8d9bdb235 ("drm/qxl: add support for > 1 output"). qxl_num_crtc defaults to 4 and is configurable as a module parameter. Signed-off-by: Gabriel Krisman Bertazi Acked-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170127010548.27970-1-krisman@collabora.co.uk Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/qxl/qxl_fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index e6ade6aab54c..4ba2d2f81bd3 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -399,7 +399,7 @@ int qxl_fbdev_init(struct qxl_device *qdev) &qxl_fb_helper_funcs); ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper, - qxl_num_crtc /* num_crtc - QXL supports just 1 */, + qxl_num_crtc, QXLFB_CONN_LIMIT); if (ret) goto free; From 5472bdee3f7ac3393e6911fc21eaa339b978cfed Mon Sep 17 00:00:00 2001 From: Gabriel Krisman Bertazi Date: Thu, 26 Jan 2017 23:05:46 -0200 Subject: [PATCH 13/47] drm: qxl: Drop duplicated device pointer attribute qxl_device duplicates a pointer to struct device, which is not needed since we already have it in the drm_device structure. Clean it up. Signed-off-by: Gabriel Krisman Bertazi Link: http://patchwork.freedesktop.org/patch/msgid/20170127010548.27970-2-krisman@collabora.co.uk Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/qxl/qxl_drv.h | 1 - drivers/gpu/drm/qxl/qxl_kms.c | 1 - drivers/gpu/drm/qxl/qxl_object.c | 16 ++++++++-------- drivers/gpu/drm/qxl/qxl_object.h | 8 ++++---- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 0d877fa61162..a022de205134 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -242,7 +242,6 @@ void qxl_debugfs_remove_files(struct qxl_device *qdev); struct qxl_device; struct qxl_device { - struct device *dev; struct drm_device *ddev; struct pci_dev *pdev; unsigned long flags; diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index d0666f5dccd6..7632e38493fc 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -122,7 +122,6 @@ int qxl_device_init(struct qxl_device *qdev, { int r, sb; - qdev->dev = &pdev->dev; qdev->ddev = ddev; qdev->pdev = pdev; qdev->flags = flags; diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c index fa5440dc9a19..adaa131d1588 100644 --- a/drivers/gpu/drm/qxl/qxl_object.c +++ b/drivers/gpu/drm/qxl/qxl_object.c @@ -113,7 +113,7 @@ int qxl_bo_create(struct qxl_device *qdev, NULL, NULL, &qxl_ttm_bo_destroy); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) - dev_err(qdev->dev, + dev_err(qdev->ddev->dev, "object_init failed for (%lu, 0x%08X)\n", size, domain); return r; @@ -223,7 +223,7 @@ struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo) int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) { - struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; + struct drm_device *ddev = bo->gem_base.dev; int r; if (bo->pin_count) { @@ -240,17 +240,17 @@ int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) *gpu_addr = qxl_bo_gpu_offset(bo); } if (unlikely(r != 0)) - dev_err(qdev->dev, "%p pin failed\n", bo); + dev_err(ddev->dev, "%p pin failed\n", bo); return r; } int qxl_bo_unpin(struct qxl_bo *bo) { - struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; + struct drm_device *ddev = bo->gem_base.dev; int r, i; if (!bo->pin_count) { - dev_warn(qdev->dev, "%p unpin not necessary\n", bo); + dev_warn(ddev->dev, "%p unpin not necessary\n", bo); return 0; } bo->pin_count--; @@ -260,7 +260,7 @@ int qxl_bo_unpin(struct qxl_bo *bo) bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); if (unlikely(r != 0)) - dev_err(qdev->dev, "%p validate failed for unpin\n", bo); + dev_err(ddev->dev, "%p validate failed for unpin\n", bo); return r; } @@ -270,9 +270,9 @@ void qxl_bo_force_delete(struct qxl_device *qdev) if (list_empty(&qdev->gem.objects)) return; - dev_err(qdev->dev, "Userspace still has active objects !\n"); + dev_err(qdev->ddev->dev, "Userspace still has active objects !\n"); list_for_each_entry_safe(bo, n, &qdev->gem.objects, list) { - dev_err(qdev->dev, "%p %p %lu %lu force free\n", + dev_err(qdev->ddev->dev, "%p %p %lu %lu force free\n", &bo->gem_base, bo, (unsigned long)bo->gem_base.size, *((unsigned long *)&bo->gem_base.refcount)); mutex_lock(&qdev->gem.mutex); diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h index 4d8311373ba3..0374fd93f4d6 100644 --- a/drivers/gpu/drm/qxl/qxl_object.h +++ b/drivers/gpu/drm/qxl/qxl_object.h @@ -34,8 +34,8 @@ static inline int qxl_bo_reserve(struct qxl_bo *bo, bool no_wait) r = ttm_bo_reserve(&bo->tbo, true, no_wait, NULL); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) { - struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; - dev_err(qdev->dev, "%p reserve failed\n", bo); + struct drm_device *ddev = bo->gem_base.dev; + dev_err(ddev->dev, "%p reserve failed\n", bo); } return r; } @@ -70,8 +70,8 @@ static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type, r = ttm_bo_reserve(&bo->tbo, true, no_wait, NULL); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) { - struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; - dev_err(qdev->dev, "%p reserve failed for wait\n", + struct drm_device *ddev = bo->gem_base.dev; + dev_err(ddev->dev, "%p reserve failed for wait\n", bo); } return r; From d74d74aeaf0b29a158bc95c63c6b6b107a57f4f5 Mon Sep 17 00:00:00 2001 From: Gabriel Krisman Bertazi Date: Thu, 26 Jan 2017 23:05:47 -0200 Subject: [PATCH 14/47] drm: qxl: Drop duplicated pci_device pointer attribute qxl_device duplicates the pointer to struct pci_dev, which is not needed since we already have it in the drm_device structure. Clean it up. Signed-off-by: Gabriel Krisman Bertazi Link: http://patchwork.freedesktop.org/patch/msgid/20170127010548.27970-3-krisman@collabora.co.uk Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/qxl/qxl_drv.h | 1 - drivers/gpu/drm/qxl/qxl_ioctl.c | 2 +- drivers/gpu/drm/qxl/qxl_kms.c | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index a022de205134..05a96dde1916 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -243,7 +243,6 @@ struct qxl_device; struct qxl_device { struct drm_device *ddev; - struct pci_dev *pdev; unsigned long flags; resource_size_t vram_base, vram_size; diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c index 5a4c8c492683..7ad5070f2550 100644 --- a/drivers/gpu/drm/qxl/qxl_ioctl.c +++ b/drivers/gpu/drm/qxl/qxl_ioctl.c @@ -375,7 +375,7 @@ static int qxl_clientcap_ioctl(struct drm_device *dev, void *data, byte = param->index / 8; idx = param->index % 8; - if (qdev->pdev->revision < 4) + if (dev->pdev->revision < 4) return -ENOSYS; if (byte >= 58) diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index 7632e38493fc..52d1bc1bb0ca 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -123,7 +123,6 @@ int qxl_device_init(struct qxl_device *qdev, int r, sb; qdev->ddev = ddev; - qdev->pdev = pdev; qdev->flags = flags; mutex_init(&qdev->gem.mutex); From cbdded7f8a633ee9418047c50a7114ce2282b912 Mon Sep 17 00:00:00 2001 From: Gabriel Krisman Bertazi Date: Thu, 26 Jan 2017 23:05:48 -0200 Subject: [PATCH 15/47] drm: qxl: Embed drm_device into driver structure This is the recommended way to create the drm_device structure, according to DRM documentation. Signed-off-by: Gabriel Krisman Bertazi Link: http://patchwork.freedesktop.org/patch/msgid/20170127010548.27970-4-krisman@collabora.co.uk Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/qxl/qxl_debugfs.c | 6 +++--- drivers/gpu/drm/qxl/qxl_display.c | 32 +++++++++++++++---------------- drivers/gpu/drm/qxl/qxl_drv.c | 30 +++++++++-------------------- drivers/gpu/drm/qxl/qxl_drv.h | 6 +++--- drivers/gpu/drm/qxl/qxl_fb.c | 10 +++++----- drivers/gpu/drm/qxl/qxl_ioctl.c | 2 +- drivers/gpu/drm/qxl/qxl_irq.c | 2 +- drivers/gpu/drm/qxl/qxl_kms.c | 11 +++++++++-- drivers/gpu/drm/qxl/qxl_object.c | 8 ++++---- drivers/gpu/drm/qxl/qxl_ttm.c | 2 +- 10 files changed, 52 insertions(+), 57 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_debugfs.c b/drivers/gpu/drm/qxl/qxl_debugfs.c index 057b2b547cac..d58751c94618 100644 --- a/drivers/gpu/drm/qxl/qxl_debugfs.c +++ b/drivers/gpu/drm/qxl/qxl_debugfs.c @@ -133,8 +133,8 @@ int qxl_debugfs_add_files(struct qxl_device *qdev, qdev->debugfs_count = i; #if defined(CONFIG_DEBUG_FS) drm_debugfs_create_files(files, nfiles, - qdev->ddev->primary->debugfs_root, - qdev->ddev->primary); + qdev->ddev.primary->debugfs_root, + qdev->ddev.primary); #endif return 0; } @@ -147,7 +147,7 @@ void qxl_debugfs_remove_files(struct qxl_device *qdev) for (i = 0; i < qdev->debugfs_count; i++) { drm_debugfs_remove_files(qdev->debugfs[i].files, qdev->debugfs[i].num_files, - qdev->ddev->primary); + qdev->ddev.primary); } #endif } diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 416ade8566b7..1094cd33eb06 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -136,7 +136,7 @@ static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) static void qxl_update_offset_props(struct qxl_device *qdev) { - struct drm_device *dev = qdev->ddev; + struct drm_device *dev = &qdev->ddev; struct drm_connector *connector; struct qxl_output *output; struct qxl_head *head; @@ -156,7 +156,7 @@ static void qxl_update_offset_props(struct qxl_device *qdev) void qxl_display_read_client_monitors_config(struct qxl_device *qdev) { - struct drm_device *dev = qdev->ddev; + struct drm_device *dev = &qdev->ddev; int status; status = qxl_display_copy_rom_client_monitors_config(qdev); @@ -174,10 +174,10 @@ void qxl_display_read_client_monitors_config(struct qxl_device *qdev) drm_modeset_lock_all(dev); qxl_update_offset_props(qdev); drm_modeset_unlock_all(dev); - if (!drm_helper_hpd_irq_event(qdev->ddev)) { + if (!drm_helper_hpd_irq_event(dev)) { /* notify that the monitor configuration changed, to adjust at the arbitrary resolution */ - drm_kms_helper_hotplug_event(qdev->ddev); + drm_kms_helper_hotplug_event(dev); } } @@ -1036,7 +1036,7 @@ static int qxl_mode_create_hotplug_mode_update_property(struct qxl_device *qdev) return 0; qdev->hotplug_mode_update_property = - drm_property_create_range(qdev->ddev, DRM_MODE_PROP_IMMUTABLE, + drm_property_create_range(&qdev->ddev, DRM_MODE_PROP_IMMUTABLE, "hotplug_mode_update", 0, 1); return 0; @@ -1175,28 +1175,28 @@ int qxl_modeset_init(struct qxl_device *qdev) int i; int ret; - drm_mode_config_init(qdev->ddev); + drm_mode_config_init(&qdev->ddev); ret = qxl_create_monitors_object(qdev); if (ret) return ret; - qdev->ddev->mode_config.funcs = (void *)&qxl_mode_funcs; + qdev->ddev.mode_config.funcs = (void *)&qxl_mode_funcs; /* modes will be validated against the framebuffer size */ - qdev->ddev->mode_config.min_width = 320; - qdev->ddev->mode_config.min_height = 200; - qdev->ddev->mode_config.max_width = 8192; - qdev->ddev->mode_config.max_height = 8192; + qdev->ddev.mode_config.min_width = 320; + qdev->ddev.mode_config.min_height = 200; + qdev->ddev.mode_config.max_width = 8192; + qdev->ddev.mode_config.max_height = 8192; - qdev->ddev->mode_config.fb_base = qdev->vram_base; + qdev->ddev.mode_config.fb_base = qdev->vram_base; - drm_mode_create_suggested_offset_properties(qdev->ddev); + drm_mode_create_suggested_offset_properties(&qdev->ddev); qxl_mode_create_hotplug_mode_update_property(qdev); for (i = 0 ; i < qxl_num_crtc; ++i) { - qdev_crtc_init(qdev->ddev, i); - qdev_output_init(qdev->ddev, i); + qdev_crtc_init(&qdev->ddev, i); + qdev_output_init(&qdev->ddev, i); } qdev->mode_info.mode_config_initialized = true; @@ -1214,7 +1214,7 @@ void qxl_modeset_fini(struct qxl_device *qdev) qxl_destroy_monitors_object(qdev); if (qdev->mode_info.mode_config_initialized) { - drm_mode_config_cleanup(qdev->ddev); + drm_mode_config_cleanup(&qdev->ddev); qdev->mode_info.mode_config_initialized = false; } } diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index 6e0f8a2d8ac9..8e17c241e63c 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -62,7 +62,6 @@ static struct pci_driver qxl_pci_driver; static int qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - struct drm_device *drm; struct qxl_device *qdev; int ret; @@ -72,29 +71,19 @@ qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return -EINVAL; /* TODO: ENODEV ? */ } - drm = drm_dev_alloc(&qxl_driver, &pdev->dev); - if (IS_ERR(drm)) - return -ENOMEM; - qdev = kzalloc(sizeof(struct qxl_device), GFP_KERNEL); - if (!qdev) { - ret = -ENOMEM; - goto free_drm_device; - } + if (!qdev) + return -ENOMEM; ret = pci_enable_device(pdev); if (ret) - goto free_drm_device; + goto free_dev; - drm->pdev = pdev; - pci_set_drvdata(pdev, drm); - drm->dev_private = qdev; - - ret = qxl_device_init(qdev, drm, pdev, ent->driver_data); + ret = qxl_device_init(qdev, &qxl_driver, pdev, ent->driver_data); if (ret) goto disable_pci; - ret = drm_vblank_init(drm, 1); + ret = drm_vblank_init(&qdev->ddev, 1); if (ret) goto unload; @@ -102,10 +91,10 @@ qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) goto vblank_cleanup; - drm_kms_helper_poll_init(qdev->ddev); + drm_kms_helper_poll_init(&qdev->ddev); /* Complete initialization. */ - ret = drm_dev_register(drm, ent->driver_data); + ret = drm_dev_register(&qdev->ddev, ent->driver_data); if (ret) goto modeset_cleanup; @@ -114,14 +103,13 @@ qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) modeset_cleanup: qxl_modeset_fini(qdev); vblank_cleanup: - drm_vblank_cleanup(drm); + drm_vblank_cleanup(&qdev->ddev); unload: qxl_device_fini(qdev); disable_pci: pci_disable_device(pdev); -free_drm_device: +free_dev: kfree(qdev); - kfree(drm); return ret; } diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 05a96dde1916..785c17b56f73 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -242,7 +242,7 @@ void qxl_debugfs_remove_files(struct qxl_device *qdev); struct qxl_device; struct qxl_device { - struct drm_device *ddev; + struct drm_device ddev; unsigned long flags; resource_size_t vram_base, vram_size; @@ -334,8 +334,8 @@ __printf(2,3) void qxl_io_log(struct qxl_device *qdev, const char *fmt, ...); extern const struct drm_ioctl_desc qxl_ioctls[]; extern int qxl_max_ioctl; -int qxl_device_init(struct qxl_device *qdev, struct drm_device *ddev, - struct pci_dev *pdev, unsigned long flags); +int qxl_device_init(struct qxl_device *qdev, struct drm_driver *drv, + struct pci_dev *pdev, unsigned long flags); void qxl_device_fini(struct qxl_device *qdev); int qxl_modeset_init(struct qxl_device *qdev); diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index 4ba2d2f81bd3..53edfa498af5 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -268,7 +268,7 @@ static int qxlfb_create(struct qxl_fbdev *qfbdev, info->par = qfbdev; - qxl_framebuffer_init(qdev->ddev, &qfbdev->qfb, &mode_cmd, gobj, + qxl_framebuffer_init(&qdev->ddev, &qfbdev->qfb, &mode_cmd, gobj, &qxlfb_fb_funcs); fb = &qfbdev->qfb.base; @@ -297,7 +297,7 @@ static int qxlfb_create(struct qxl_fbdev *qfbdev, sizes->fb_height); /* setup aperture base/size for vesafb takeover */ - info->apertures->ranges[0].base = qdev->ddev->mode_config.fb_base; + info->apertures->ranges[0].base = qdev->ddev.mode_config.fb_base; info->apertures->ranges[0].size = qdev->vram_size; info->fix.mmio_start = 0; @@ -395,10 +395,10 @@ int qxl_fbdev_init(struct qxl_device *qdev) spin_lock_init(&qfbdev->delayed_ops_lock); INIT_LIST_HEAD(&qfbdev->delayed_ops); - drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper, + drm_fb_helper_prepare(&qdev->ddev, &qfbdev->helper, &qxl_fb_helper_funcs); - ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper, + ret = drm_fb_helper_init(&qdev->ddev, &qfbdev->helper, qxl_num_crtc, QXLFB_CONN_LIMIT); if (ret) @@ -426,7 +426,7 @@ void qxl_fbdev_fini(struct qxl_device *qdev) if (!qdev->mode_info.qfbdev) return; - qxl_fbdev_destroy(qdev->ddev, qdev->mode_info.qfbdev); + qxl_fbdev_destroy(&qdev->ddev, qdev->mode_info.qfbdev); kfree(qdev->mode_info.qfbdev); qdev->mode_info.qfbdev = NULL; } diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c index 7ad5070f2550..0b82a87916ae 100644 --- a/drivers/gpu/drm/qxl/qxl_ioctl.c +++ b/drivers/gpu/drm/qxl/qxl_ioctl.c @@ -64,7 +64,7 @@ static int qxl_map_ioctl(struct drm_device *dev, void *data, struct qxl_device *qdev = dev->dev_private; struct drm_qxl_map *qxl_map = data; - return qxl_mode_dumb_mmap(file_priv, qdev->ddev, qxl_map->handle, + return qxl_mode_dumb_mmap(file_priv, &qdev->ddev, qxl_map->handle, &qxl_map->offset); } diff --git a/drivers/gpu/drm/qxl/qxl_irq.c b/drivers/gpu/drm/qxl/qxl_irq.c index 0bf1e20c6e44..23a40106ab53 100644 --- a/drivers/gpu/drm/qxl/qxl_irq.c +++ b/drivers/gpu/drm/qxl/qxl_irq.c @@ -90,7 +90,7 @@ int qxl_irq_init(struct qxl_device *qdev) atomic_set(&qdev->irq_received_cursor, 0); atomic_set(&qdev->irq_received_io_cmd, 0); qdev->irq_received_error = 0; - ret = drm_irq_install(qdev->ddev, qdev->ddev->pdev->irq); + ret = drm_irq_install(&qdev->ddev, qdev->ddev.pdev->irq); qdev->ram_header->int_mask = QXL_INTERRUPT_MASK; if (unlikely(ret != 0)) { DRM_ERROR("Failed installing irq: %d\n", ret); diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index 52d1bc1bb0ca..2dcd5c14cb56 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -116,13 +116,20 @@ static void qxl_gc_work(struct work_struct *work) } int qxl_device_init(struct qxl_device *qdev, - struct drm_device *ddev, + struct drm_driver *drv, struct pci_dev *pdev, unsigned long flags) { int r, sb; - qdev->ddev = ddev; + r = drm_dev_init(&qdev->ddev, drv, &pdev->dev); + if (r) + return r; + + qdev->ddev.pdev = pdev; + pci_set_drvdata(pdev, &qdev->ddev); + qdev->ddev.dev_private = qdev; + qdev->flags = flags; mutex_init(&qdev->gem.mutex); diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c index adaa131d1588..dbc13510a1f8 100644 --- a/drivers/gpu/drm/qxl/qxl_object.c +++ b/drivers/gpu/drm/qxl/qxl_object.c @@ -93,7 +93,7 @@ int qxl_bo_create(struct qxl_device *qdev, if (bo == NULL) return -ENOMEM; size = roundup(size, PAGE_SIZE); - r = drm_gem_object_init(qdev->ddev, &bo->gem_base, size); + r = drm_gem_object_init(&qdev->ddev, &bo->gem_base, size); if (unlikely(r)) { kfree(bo); return r; @@ -113,7 +113,7 @@ int qxl_bo_create(struct qxl_device *qdev, NULL, NULL, &qxl_ttm_bo_destroy); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) - dev_err(qdev->ddev->dev, + dev_err(qdev->ddev.dev, "object_init failed for (%lu, 0x%08X)\n", size, domain); return r; @@ -270,9 +270,9 @@ void qxl_bo_force_delete(struct qxl_device *qdev) if (list_empty(&qdev->gem.objects)) return; - dev_err(qdev->ddev->dev, "Userspace still has active objects !\n"); + dev_err(qdev->ddev.dev, "Userspace still has active objects !\n"); list_for_each_entry_safe(bo, n, &qdev->gem.objects, list) { - dev_err(qdev->ddev->dev, "%p %p %lu %lu force free\n", + dev_err(qdev->ddev.dev, "%p %p %lu %lu force free\n", &bo->gem_base, bo, (unsigned long)bo->gem_base.size, *((unsigned long *)&bo->gem_base.refcount)); mutex_lock(&qdev->gem.mutex); diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index 3dcc48431015..4e1a40389964 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -408,7 +408,7 @@ int qxl_ttm_init(struct qxl_device *qdev) r = ttm_bo_device_init(&qdev->mman.bdev, qdev->mman.bo_global_ref.ref.object, &qxl_bo_driver, - qdev->ddev->anon_inode->i_mapping, + qdev->ddev.anon_inode->i_mapping, DRM_FILE_PAGE_OFFSET, 0); if (r) { DRM_ERROR("failed initializing buffer object driver(%d).\n", r); From dd12312906b33053319cdc0bb5c247185d24bffe Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:28 +0100 Subject: [PATCH 16/47] drm/bridge/sii8620: simplify MHL3 mode setting It is not necessary to set REG_COC_CTL0, REG_MHL_COC_CTL1 registers. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-2-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index b2c267df7ee7..68cdf636c891 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -974,12 +974,8 @@ static void sii8620_set_mode(struct sii8620 *ctx, enum sii8620_mode mode) ); break; case CM_MHL3: - sii8620_write_seq_static(ctx, - REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE, - REG_COC_CTL0, 0x40, - REG_MHL_COC_CTL1, 0x07 - ); - break; + sii8620_write(ctx, REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE); + return; case CM_DISCONNECTED: break; default: From bb4954c77c2515f383f8828b18c2a7357455d3d1 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:29 +0100 Subject: [PATCH 17/47] drm/bridge/sii8620: add MHL3 mode check helper MHL3 modes differs significantly from MHL1 mode, this helper will be used frequently to clearly distinguish them. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-3-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 68cdf636c891..50af78f07d9c 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -227,6 +227,11 @@ static void sii8620_setbits(struct sii8620 *ctx, u16 addr, u8 mask, u8 val) sii8620_write(ctx, addr, val); } +static inline bool sii8620_is_mhl3(struct sii8620 *ctx) +{ + return ctx->mode >= CM_MHL3; +} + static void sii8620_mt_cleanup(struct sii8620 *ctx) { struct sii8620_mt_msg *msg, *n; @@ -724,7 +729,7 @@ static void sii8620_start_hdmi(struct sii8620 *ctx) static void sii8620_start_video(struct sii8620 *ctx) { - if (ctx->mode < CM_MHL3) + if (!sii8620_is_mhl3(ctx)) sii8620_stop_video(ctx); switch (ctx->sink_type) { @@ -838,7 +843,7 @@ static void sii8620_mhl_discover(struct sii8620 *ctx) static void sii8620_peer_specific_init(struct sii8620 *ctx) { - if (ctx->mode == CM_MHL3) + if (sii8620_is_mhl3(ctx)) sii8620_write_seq_static(ctx, REG_SYS_CTRL1, BIT_SYS_CTRL1_BLOCK_DDC_BY_HPD, REG_EMSCINTRMASK1, From e9c6da270aaae54fb67aeb5858c456f3beffa6d9 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:30 +0100 Subject: [PATCH 18/47] drm/bridge/sii8620: add reading device capability registers This functionality is necessary to implement MHL3 modes. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-4-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 50af78f07d9c..75867c0735de 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -286,6 +286,12 @@ static void sii8620_mt_msc_cmd_send(struct sii8620 *ctx, sii8620_write(ctx, REG_MSC_COMMAND_START, BIT_MSC_COMMAND_START_MSC_MSG); break; + case MHL_READ_DEVCAP_REG: + case MHL_READ_XDEVCAP_REG: + sii8620_write(ctx, REG_MSC_CMD_OR_OFFSET, msg->reg[1]); + sii8620_write(ctx, REG_MSC_COMMAND_START, + BIT_MSC_COMMAND_START_READ_DEVCAP); + break; default: dev_err(ctx->dev, "%s: command %#x not supported\n", __func__, msg->reg[0]); @@ -455,6 +461,35 @@ static void sii8620_mt_read_devcap(struct sii8620 *ctx, bool xdevcap) msg->recv = sii8620_mt_read_devcap_recv; } +static void sii8620_mt_read_devcap_reg_recv(struct sii8620 *ctx, + struct sii8620_mt_msg *msg) +{ + u8 reg = msg->reg[0] & 0x7f; + + if (msg->reg[0] & 0x80) + ctx->xdevcap[reg] = msg->ret; + else + ctx->devcap[reg] = msg->ret; +} + +static void sii8620_mt_read_devcap_reg(struct sii8620 *ctx, u8 reg) +{ + struct sii8620_mt_msg *msg = sii8620_mt_msg_new(ctx); + + if (!msg) + return; + + msg->reg[0] = (reg & 0x80) ? MHL_READ_XDEVCAP_REG : MHL_READ_DEVCAP_REG; + msg->reg[1] = reg; + msg->send = sii8620_mt_msc_cmd_send; + msg->recv = sii8620_mt_read_devcap_reg_recv; +} + +static inline void sii8620_mt_read_xdevcap_reg(struct sii8620 *ctx, u8 reg) +{ + sii8620_mt_read_devcap_reg(ctx, reg | 0x80); +} + static void sii8620_fetch_edid(struct sii8620 *ctx) { u8 lm_ddc, ddc_cmd, int3, cbus; From 0c2d18756b5b54f2ae5d00ff841c3db608d33fb9 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:31 +0100 Subject: [PATCH 19/47] drm/bridge/sii8620: add continuations to messages Due to asynchronous nature of MHL flow of execution is dispersed. Logical continuation of some actions happens after response of peer, i.e in interrupt handler. To simplify coding continuation mechanism has been added - it is now possible to provide continuation callback, which will be called after peer responds to given action. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-5-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 75867c0735de..cde007489375 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -78,12 +78,15 @@ struct sii8620_mt_msg; typedef void (*sii8620_mt_msg_cb)(struct sii8620 *ctx, struct sii8620_mt_msg *msg); +typedef void (*sii8620_cb)(struct sii8620 *ctx, int ret); + struct sii8620_mt_msg { struct list_head node; u8 reg[4]; u8 ret; sii8620_mt_msg_cb send; sii8620_mt_msg_cb recv; + sii8620_cb continuation; }; static const u8 sii8620_i2c_page[] = { @@ -258,6 +261,8 @@ static void sii8620_mt_work(struct sii8620 *ctx) node); if (msg->recv) msg->recv(ctx, msg); + if (msg->continuation) + msg->continuation(ctx, msg->ret); list_del(&msg->node); kfree(msg); } @@ -310,6 +315,21 @@ static struct sii8620_mt_msg *sii8620_mt_msg_new(struct sii8620 *ctx) return msg; } +static void sii8620_mt_set_cont(struct sii8620 *ctx, sii8620_cb cont) +{ + struct sii8620_mt_msg *msg; + + if (ctx->error) + return; + + if (list_empty(&ctx->mt_queue)) { + ctx->error = -EINVAL; + return; + } + msg = list_last_entry(&ctx->mt_queue, struct sii8620_mt_msg, node); + msg->continuation = cont; +} + static void sii8620_mt_msc_cmd(struct sii8620 *ctx, u8 cmd, u8 arg1, u8 arg2) { struct sii8620_mt_msg *msg = sii8620_mt_msg_new(ctx); From 2c8fb853c063181bf80154e2fc3474d6f8c4a89c Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:32 +0100 Subject: [PATCH 20/47] drm/bridge/sii8620: initial support for eCBUS-S mode The Single-ended eCBUS Mode (eCBUS-S) mode provides 60 Mb/s full-duplex bidirectional traffic for three channels: - CBUS data (CBUS1 channel), - High-bandwidth MHL data (eMSC channel), - tunneling data (T-CBUS channel). It is required to fully support MHL3 dongles. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-6-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 104 ++++++++++++++++++++++++++- drivers/gpu/drm/bridge/sil-sii8620.h | 5 ++ 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index cde007489375..0d3716f3def3 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -104,6 +104,7 @@ static void sii8620_fetch_edid(struct sii8620 *ctx); static void sii8620_set_upstream_edid(struct sii8620 *ctx); static void sii8620_enable_hpd(struct sii8620 *ctx); static void sii8620_mhl_disconnected(struct sii8620 *ctx); +static void sii8620_disconnect(struct sii8620 *ctx); static int sii8620_clear_error(struct sii8620 *ctx) { @@ -1016,13 +1017,44 @@ static void sii8620_mhl_init(struct sii8620 *ctx) sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), MHL_INT_RC_DCAP_CHG); } +static void sii8620_emsc_enable(struct sii8620 *ctx) +{ + u8 reg; + + sii8620_setbits(ctx, REG_GENCTL, BIT_GENCTL_EMSC_EN + | BIT_GENCTL_CLR_EMSC_RFIFO + | BIT_GENCTL_CLR_EMSC_XFIFO, ~0); + sii8620_setbits(ctx, REG_GENCTL, BIT_GENCTL_CLR_EMSC_RFIFO + | BIT_GENCTL_CLR_EMSC_XFIFO, 0); + sii8620_setbits(ctx, REG_COMMECNT, BIT_COMMECNT_I2C_TO_EMSC_EN, ~0); + reg = sii8620_readb(ctx, REG_EMSCINTR); + sii8620_write(ctx, REG_EMSCINTR, reg); + sii8620_write(ctx, REG_EMSCINTRMASK, BIT_EMSCINTR_SPI_DVLD); +} + +static int sii8620_wait_for_fsm_state(struct sii8620 *ctx, u8 state) +{ + int i; + + for (i = 0; i < 10; ++i) { + u8 s = sii8620_readb(ctx, REG_COC_STAT_0); + + if ((s & MSK_COC_STAT_0_FSM_STATE) == state) + return 0; + if (!(s & BIT_COC_STAT_0_PLL_LOCKED)) + return -EBUSY; + usleep_range(4000, 6000); + } + return -ETIMEDOUT; +} + static void sii8620_set_mode(struct sii8620 *ctx, enum sii8620_mode mode) { + int ret; + if (ctx->mode == mode) return; - ctx->mode = mode; - switch (mode) { case CM_MHL1: sii8620_write_seq_static(ctx, @@ -1032,11 +1064,46 @@ static void sii8620_set_mode(struct sii8620 *ctx, enum sii8620_mode mode) | BIT_DPD_OSC_EN, REG_COC_INTR_MASK, 0 ); + ctx->mode = mode; break; case CM_MHL3: sii8620_write(ctx, REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE); + ctx->mode = mode; + return; + case CM_ECBUS_S: + sii8620_emsc_enable(ctx); + sii8620_write_seq_static(ctx, + REG_TTXSPINUMS, 4, + REG_TRXSPINUMS, 4, + REG_TTXHSICNUMS, 0x14, + REG_TRXHSICNUMS, 0x14, + REG_TTXTOTNUMS, 0x18, + REG_TRXTOTNUMS, 0x18, + REG_PWD_SRST, BIT_PWD_SRST_COC_DOC_RST + | BIT_PWD_SRST_CBUS_RST_SW_EN, + REG_MHL_COC_CTL1, 0xbd, + REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN, + REG_COC_CTLB, 0x01, + REG_COC_CTL0, 0x5c, + REG_COC_CTL14, 0x03, + REG_COC_CTL15, 0x80, + REG_MHL_DP_CTL6, BIT_MHL_DP_CTL6_DP_TAP1_SGN + | BIT_MHL_DP_CTL6_DP_TAP1_EN + | BIT_MHL_DP_CTL6_DT_PREDRV_FEEDCAP_EN, + REG_MHL_DP_CTL8, 0x03 + ); + ret = sii8620_wait_for_fsm_state(ctx, 0x03); + sii8620_write_seq_static(ctx, + REG_COC_CTL14, 0x00, + REG_COC_CTL15, 0x80 + ); + if (!ret) + sii8620_write(ctx, REG_CBUS3_CNVT, 0x85); + else + sii8620_disconnect(ctx); return; case CM_DISCONNECTED: + ctx->mode = mode; break; default: dev_err(ctx->dev, "%s mode %d not supported\n", __func__, mode); @@ -1229,12 +1296,45 @@ static void sii8620_msc_mr_write_stat(struct sii8620 *ctx) sii8620_status_changed_path(ctx); } +static void sii8620_ecbus_up(struct sii8620 *ctx, int ret) +{ + if (ret < 0) + return; + + sii8620_set_mode(ctx, CM_ECBUS_S); +} + +static void sii8620_got_ecbus_speed(struct sii8620 *ctx, int ret) +{ + if (ret < 0) + return; + + sii8620_mt_write_stat(ctx, MHL_XDS_REG(CURR_ECBUS_MODE), + MHL_XDS_ECBUS_S | MHL_XDS_SLOT_MODE_8BIT); + sii8620_mt_rap(ctx, MHL_RAP_CBUS_MODE_UP); + sii8620_mt_set_cont(ctx, sii8620_ecbus_up); +} + static void sii8620_msc_mr_set_int(struct sii8620 *ctx) { u8 ints[MHL_INT_SIZE]; sii8620_read_buf(ctx, REG_MHL_INT_0, ints, MHL_INT_SIZE); sii8620_write_buf(ctx, REG_MHL_INT_0, ints, MHL_INT_SIZE); + + if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_DCAP_CHG) { + switch (ctx->mode) { + case CM_MHL3: + sii8620_mt_read_xdevcap_reg(ctx, MHL_XDC_ECBUS_SPEEDS); + sii8620_mt_set_cont(ctx, sii8620_got_ecbus_speed); + break; + case CM_ECBUS_S: + sii8620_mt_read_devcap(ctx, true); + break; + default: + break; + } + } } static struct sii8620_mt_msg *sii8620_msc_msg_first(struct sii8620 *ctx) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.h b/drivers/gpu/drm/bridge/sil-sii8620.h index 6ff616a4f6ce..3ee4e7e42226 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.h +++ b/drivers/gpu/drm/bridge/sil-sii8620.h @@ -841,6 +841,8 @@ #define MSK_MHL_DP_CTL7_DT_DRV_VBIAS_CASCTL 0xf0 #define MSK_MHL_DP_CTL7_DT_DRV_IREF_CTL 0x0f +#define REG_MHL_DP_CTL8 0x0352 + /* Tx Zone Ctl1, default value: 0x00 */ #define REG_TX_ZONE_CTL1 0x0361 #define VAL_TX_ZONE_CTL1_TX_ZONE_CTRL_MODE 0x08 @@ -1088,6 +1090,9 @@ /* CoC Status, default value: 0x00 */ #define REG_COC_STAT_0 0x0700 +#define BIT_COC_STAT_0_PLL_LOCKED BIT(7) +#define MSK_COC_STAT_0_FSM_STATE 0x0f + #define REG_COC_STAT_1 0x0701 #define REG_COC_STAT_2 0x0702 #define REG_COC_STAT_3 0x0703 From 981b3f372458220fca74cd79d61375a5b793b578 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:33 +0100 Subject: [PATCH 21/47] drm/bridge/mhl: add write burst related definitions Burst transmissions are required in MHL3 modes. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-7-git-send-email-a.hajda@samsung.com --- include/drm/bridge/mhl.h | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/include/drm/bridge/mhl.h b/include/drm/bridge/mhl.h index 3629b2734db6..e8d194da4b2c 100644 --- a/include/drm/bridge/mhl.h +++ b/include/drm/bridge/mhl.h @@ -15,6 +15,8 @@ #ifndef __MHL_H__ #define __MHL_H__ +#include + /* Device Capabilities Registers */ enum { MHL_DCAP_DEV_STATE, @@ -288,4 +290,55 @@ enum { /* Unsupported/unrecognized key code */ #define MHL_UCPE_STATUS_INEFFECTIVE_KEY_CODE 0x01 +enum mhl_burst_id { + MHL_BURST_ID_3D_VIC = 0x10, + MHL_BURST_ID_3D_DTD = 0x11, + MHL_BURST_ID_HEV_VIC = 0x20, + MHL_BURST_ID_HEV_DTDA = 0x21, + MHL_BURST_ID_HEV_DTDB = 0x22, + MHL_BURST_ID_VC_ASSIGN = 0x38, + MHL_BURST_ID_VC_CONFIRM = 0x39, + MHL_BURST_ID_AUD_DELAY = 0x40, + MHL_BURST_ID_ADT_BURSTID = 0x41, + MHL_BURST_ID_BIST_SETUP = 0x51, + MHL_BURST_ID_BIST_RETURN_STAT = 0x52, + MHL_BURST_ID_EMSC_SUPPORT = 0x61, + MHL_BURST_ID_HID_PAYLOAD = 0x62, + MHL_BURST_ID_BLK_RCV_BUFFER_INFO = 0x63, + MHL_BURST_ID_BITS_PER_PIXEL_FMT = 0x64, +}; + +struct mhl_burst_blk_rcv_buffer_info { + __be16 id; + __le16 size; +} __packed; + +struct mhl3_burst_header { + __be16 id; + u8 checksum; + u8 total_entries; + u8 sequence_index; +} __packed; + +struct mhl_burst_bits_per_pixel_fmt { + struct mhl3_burst_header hdr; + u8 num_entries; + struct { + u8 stream_id; + u8 pixel_format; + } __packed desc[0]; +} __packed; + +struct mhl_burst_emsc_support { + struct mhl3_burst_header hdr; + u8 num_entries; + __be16 burst_id[0]; +} __packed; + +struct mhl_burst_audio_descr { + struct mhl3_burst_header hdr; + u8 flags; + u8 short_desc[9]; +} __packed; + #endif /* __MHL_H__ */ From e19e9c692f81b87dc1414eb7945f4ad5dedbb49c Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:34 +0100 Subject: [PATCH 22/47] drm/bridge/sii8620: add support for burst eMSC transmissions Burst transmissions are used in MHL3 mode negotiation. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-8-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 194 ++++++++++++++++++++++++++- drivers/gpu/drm/bridge/sil-sii8620.h | 4 + 2 files changed, 197 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 0d3716f3def3..744e685d67ae 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -9,6 +9,8 @@ * published by the Free Software Foundation. */ +#include + #include #include #include @@ -28,7 +30,8 @@ #include "sil-sii8620.h" -#define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3) +#define SII8620_BURST_BUF_LEN 288 +#define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3) enum sii8620_mode { CM_DISCONNECTED, @@ -71,6 +74,15 @@ struct sii8620 { unsigned int gen2_write_burst:1; enum sii8620_mt_state mt_state; struct list_head mt_queue; + struct { + int r_size; + int r_count; + int rx_ack; + int rx_count; + u8 rx_buf[32]; + int tx_count; + u8 tx_buf[32]; + } burst; }; struct sii8620_mt_msg; @@ -511,6 +523,134 @@ static inline void sii8620_mt_read_xdevcap_reg(struct sii8620 *ctx, u8 reg) sii8620_mt_read_devcap_reg(ctx, reg | 0x80); } +static void *sii8620_burst_get_tx_buf(struct sii8620 *ctx, int len) +{ + u8 *buf = &ctx->burst.tx_buf[ctx->burst.tx_count]; + int size = len + 2; + + if (ctx->burst.tx_count + size > ARRAY_SIZE(ctx->burst.tx_buf)) { + dev_err(ctx->dev, "TX-BLK buffer exhausted\n"); + ctx->error = -EINVAL; + return NULL; + } + + ctx->burst.tx_count += size; + buf[1] = len; + + return buf + 2; +} + +static u8 *sii8620_burst_get_rx_buf(struct sii8620 *ctx, int len) +{ + u8 *buf = &ctx->burst.rx_buf[ctx->burst.rx_count]; + int size = len + 1; + + if (ctx->burst.tx_count + size > ARRAY_SIZE(ctx->burst.tx_buf)) { + dev_err(ctx->dev, "RX-BLK buffer exhausted\n"); + ctx->error = -EINVAL; + return NULL; + } + + ctx->burst.rx_count += size; + buf[0] = len; + + return buf + 1; +} + +static void sii8620_burst_send(struct sii8620 *ctx) +{ + int tx_left = ctx->burst.tx_count; + u8 *d = ctx->burst.tx_buf; + + while (tx_left > 0) { + int len = d[1] + 2; + + if (ctx->burst.r_count + len > ctx->burst.r_size) + break; + d[0] = min(ctx->burst.rx_ack, 255); + ctx->burst.rx_ack -= d[0]; + sii8620_write_buf(ctx, REG_EMSC_XMIT_WRITE_PORT, d, len); + ctx->burst.r_count += len; + tx_left -= len; + d += len; + } + + ctx->burst.tx_count = tx_left; + + while (ctx->burst.rx_ack > 0) { + u8 b[2] = { min(ctx->burst.rx_ack, 255), 0 }; + + if (ctx->burst.r_count + 2 > ctx->burst.r_size) + break; + ctx->burst.rx_ack -= b[0]; + sii8620_write_buf(ctx, REG_EMSC_XMIT_WRITE_PORT, b, 2); + ctx->burst.r_count += 2; + } +} + +static void sii8620_burst_receive(struct sii8620 *ctx) +{ + u8 buf[3], *d; + int count; + + sii8620_read_buf(ctx, REG_EMSCRFIFOBCNTL, buf, 2); + count = get_unaligned_le16(buf); + while (count > 0) { + int len = min(count, 3); + + sii8620_read_buf(ctx, REG_EMSC_RCV_READ_PORT, buf, len); + count -= len; + ctx->burst.rx_ack += len - 1; + ctx->burst.r_count -= buf[1]; + if (ctx->burst.r_count < 0) + ctx->burst.r_count = 0; + + if (len < 3 || !buf[2]) + continue; + + len = buf[2]; + d = sii8620_burst_get_rx_buf(ctx, len); + if (!d) + continue; + sii8620_read_buf(ctx, REG_EMSC_RCV_READ_PORT, d, len); + count -= len; + ctx->burst.rx_ack += len; + } +} + +static void sii8620_burst_tx_rbuf_info(struct sii8620 *ctx, int size) +{ + struct mhl_burst_blk_rcv_buffer_info *d = + sii8620_burst_get_tx_buf(ctx, sizeof(*d)); + if (!d) + return; + + d->id = cpu_to_be16(MHL_BURST_ID_BLK_RCV_BUFFER_INFO); + d->size = cpu_to_le16(size); +} + +static void sii8620_burst_rx_all(struct sii8620 *ctx) +{ + u8 *d = ctx->burst.rx_buf; + int count = ctx->burst.rx_count; + + while (count-- > 0) { + int len = *d++; + int id = get_unaligned_be16(&d[0]); + + switch (id) { + case MHL_BURST_ID_BLK_RCV_BUFFER_INFO: + ctx->burst.r_size = get_unaligned_le16(&d[2]); + break; + default: + break; + } + count -= len; + d += len; + } + ctx->burst.rx_count = 0; +} + static void sii8620_fetch_edid(struct sii8620 *ctx) { u8 lm_ddc, ddc_cmd, int3, cbus; @@ -1417,6 +1557,19 @@ static void sii8620_irq_coc(struct sii8620 *ctx) { u8 stat = sii8620_readb(ctx, REG_COC_INTR); + if (stat & BIT_COC_CALIBRATION_DONE) { + u8 cstat = sii8620_readb(ctx, REG_COC_STAT_0); + + cstat &= BIT_COC_STAT_0_PLL_LOCKED | MSK_COC_STAT_0_FSM_STATE; + if (cstat == (BIT_COC_STAT_0_PLL_LOCKED | 0x02)) { + sii8620_write_seq_static(ctx, + REG_COC_CTLB, 0, + REG_TRXINTMH, BIT_TDM_INTR_SYNC_DATA + | BIT_TDM_INTR_SYNC_WAIT + ); + } + } + sii8620_write(ctx, REG_COC_INTR, stat); } @@ -1507,6 +1660,41 @@ static void sii8620_irq_infr(struct sii8620 *ctx) sii8620_start_video(ctx); } +static void sii8620_irq_tdm(struct sii8620 *ctx) +{ + u8 stat = sii8620_readb(ctx, REG_TRXINTH); + u8 tdm = sii8620_readb(ctx, REG_TRXSTA2); + + if ((tdm & MSK_TDM_SYNCHRONIZED) == VAL_TDM_SYNCHRONIZED) { + ctx->mode = CM_ECBUS_S; + ctx->burst.rx_ack = 0; + ctx->burst.r_size = SII8620_BURST_BUF_LEN; + sii8620_burst_tx_rbuf_info(ctx, SII8620_BURST_BUF_LEN); + sii8620_mt_read_devcap(ctx, true); + } else { + sii8620_write_seq_static(ctx, + REG_MHL_PLL_CTL2, 0, + REG_MHL_PLL_CTL2, BIT_MHL_PLL_CTL2_CLKDETECT_EN + ); + } + + sii8620_write(ctx, REG_TRXINTH, stat); +} + +static void sii8620_irq_block(struct sii8620 *ctx) +{ + u8 stat = sii8620_readb(ctx, REG_EMSCINTR); + + if (stat & BIT_EMSCINTR_SPI_DVLD) { + u8 bstat = sii8620_readb(ctx, REG_SPIBURSTSTAT); + + if (bstat & BIT_SPIBURSTSTAT_EMSC_NORMAL_MODE) + sii8620_burst_receive(ctx); + } + + sii8620_write(ctx, REG_EMSCINTR, stat); +} + /* endian agnostic, non-volatile version of test_bit */ static bool sii8620_test_bit(unsigned int nr, const u8 *addr) { @@ -1522,8 +1710,10 @@ static irqreturn_t sii8620_irq_thread(int irq, void *data) { BIT_FAST_INTR_STAT_DISC, sii8620_irq_disc }, { BIT_FAST_INTR_STAT_G2WB, sii8620_irq_g2wb }, { BIT_FAST_INTR_STAT_COC, sii8620_irq_coc }, + { BIT_FAST_INTR_STAT_TDM, sii8620_irq_tdm }, { BIT_FAST_INTR_STAT_MSC, sii8620_irq_msc }, { BIT_FAST_INTR_STAT_MERR, sii8620_irq_merr }, + { BIT_FAST_INTR_STAT_BLOCK, sii8620_irq_block }, { BIT_FAST_INTR_STAT_EDID, sii8620_irq_edid }, { BIT_FAST_INTR_STAT_SCDT, sii8620_irq_scdt }, { BIT_FAST_INTR_STAT_INFR, sii8620_irq_infr }, @@ -1539,7 +1729,9 @@ static irqreturn_t sii8620_irq_thread(int irq, void *data) if (sii8620_test_bit(irq_vec[i].bit, stats)) irq_vec[i].handler(ctx); + sii8620_burst_rx_all(ctx); sii8620_mt_work(ctx); + sii8620_burst_send(ctx); ret = sii8620_clear_error(ctx); if (ret) { diff --git a/drivers/gpu/drm/bridge/sil-sii8620.h b/drivers/gpu/drm/bridge/sil-sii8620.h index 3ee4e7e42226..683213acc756 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.h +++ b/drivers/gpu/drm/bridge/sil-sii8620.h @@ -403,12 +403,16 @@ /* TDM RX Status 2nd, default value: 0x00 */ #define REG_TRXSTA2 0x015c +#define MSK_TDM_SYNCHRONIZED 0xc0 +#define VAL_TDM_SYNCHRONIZED 0x80 /* TDM RX INT Low, default value: 0x00 */ #define REG_TRXINTL 0x0163 /* TDM RX INT High, default value: 0x00 */ #define REG_TRXINTH 0x0164 +#define BIT_TDM_INTR_SYNC_DATA BIT(0) +#define BIT_TDM_INTR_SYNC_WAIT BIT(1) /* TDM RX INTMASK High, default value: 0x00 */ #define REG_TRXINTMH 0x0166 From 4a36888585278635d0e47c80e19c803f8fd0884e Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:35 +0100 Subject: [PATCH 23/47] drm/bridge/sii8620: respond to feature requests MHL3 protocol requires device to respond to feature request from peer. This minimal answer fulfills the requirement and allows to continue negotiation. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-9-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 744e685d67ae..384222612005 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -1475,6 +1475,10 @@ static void sii8620_msc_mr_set_int(struct sii8620 *ctx) break; } } + if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_REQ) { + sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), + MHL_INT_RC_FEAT_COMPLETE); + } } static struct sii8620_mt_msg *sii8620_msc_msg_first(struct sii8620 *ctx) From e3a654872d1b1b18eebe6a4a7e0fcb5b997f33ea Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:36 +0100 Subject: [PATCH 24/47] drm/bridge/sii8620: fix peer device capabilities read code Peer capabilities should be read differently depending on protocol version. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-10-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 384222612005..be953f3aea9c 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -1412,7 +1412,8 @@ static void sii8620_status_changed_path(struct sii8620 *ctx) sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), MHL_DST_LM_CLK_MODE_NORMAL | MHL_DST_LM_PATH_ENABLED); - sii8620_mt_read_devcap(ctx, false); + if (!sii8620_is_mhl3(ctx)) + sii8620_mt_read_devcap(ctx, false); } else { sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), MHL_DST_LM_CLK_MODE_NORMAL); @@ -1664,6 +1665,14 @@ static void sii8620_irq_infr(struct sii8620 *ctx) sii8620_start_video(ctx); } +static void sii8620_got_xdevcap(struct sii8620 *ctx, int ret) +{ + if (ret < 0) + return; + + sii8620_mt_read_devcap(ctx, false); +} + static void sii8620_irq_tdm(struct sii8620 *ctx) { u8 stat = sii8620_readb(ctx, REG_TRXINTH); @@ -1675,6 +1684,7 @@ static void sii8620_irq_tdm(struct sii8620 *ctx) ctx->burst.r_size = SII8620_BURST_BUF_LEN; sii8620_burst_tx_rbuf_info(ctx, SII8620_BURST_BUF_LEN); sii8620_mt_read_devcap(ctx, true); + sii8620_mt_set_cont(ctx, sii8620_got_xdevcap); } else { sii8620_write_seq_static(ctx, REG_MHL_PLL_CTL2, 0, From 73a5d25d500656b9642b554117b0d9c870ba76c1 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:37 +0100 Subject: [PATCH 25/47] drm/bridge/sii8620: remove spare CBUS bring-up sequence In case of MHL3 CBUS is bring-up already in sii8620_got_ecbus_speed. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-11-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index be953f3aea9c..fb69353a3252 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -453,10 +453,6 @@ static void sii8620_mr_xdevcap(struct sii8620 *ctx) { sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, ctx->xdevcap, MHL_XDC_SIZE); - - sii8620_mt_write_stat(ctx, MHL_XDS_REG(CURR_ECBUS_MODE), - MHL_XDS_ECBUS_S | MHL_XDS_SLOT_MODE_8BIT); - sii8620_mt_rap(ctx, MHL_RAP_CBUS_MODE_UP); } static void sii8620_mt_read_devcap_recv(struct sii8620 *ctx, From d6d59c5a57ebe49e4aeca8e32760c076b4cb8f7f Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:38 +0100 Subject: [PATCH 26/47] drm/bridge/sii8620: fix MSC message removal Messages queue can be cleaned up by message callbacks, to avoid repeated removal of current message it should be removed from the queue before calling these callbacks. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-12-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index fb69353a3252..a50ade6b282f 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -272,11 +272,11 @@ static void sii8620_mt_work(struct sii8620 *ctx) ctx->mt_state = MT_STATE_READY; msg = list_first_entry(&ctx->mt_queue, struct sii8620_mt_msg, node); + list_del(&msg->node); if (msg->recv) msg->recv(ctx, msg); if (msg->continuation) msg->continuation(ctx, msg->ret); - list_del(&msg->node); kfree(msg); } From ef822a0745720fc477ea87a497e847ddc2feb1f0 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:39 +0100 Subject: [PATCH 27/47] drm/bridge/sii8620: fix initialization sequence for MHL2 receivers MHL2 receiver require disabling transmitter on initialization. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-13-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index a50ade6b282f..7d2869038267 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -880,12 +880,11 @@ static void sii8620_stop_video(struct sii8620 *ctx) | BIT_TPI_SC_TPI_AV_MUTE; break; case SINK_HDMI: + default: val = BIT_TPI_SC_REG_TMDS_OE_POWER_DOWN | BIT_TPI_SC_TPI_AV_MUTE | BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI; break; - default: - return; } sii8620_write(ctx, REG_TPI_SC, val); From 9a466cd1dd0da76deef9641dbe64a59194e5ee41 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:40 +0100 Subject: [PATCH 28/47] drm/bridge/sii8620: abstract out sink detection code MHL1 and MHL3 have different initialization paths. To make both protocols happy sink detection is put into continuation after link mode enablement. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-14-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 35 ++++++++++++++++------------ 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 7d2869038267..d7a58c60b589 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -402,7 +402,7 @@ static void sii8620_update_array(u8 *dst, u8 *src, int count) } } -static void sii8620_mr_devcap(struct sii8620 *ctx) +static void sii8620_sink_detected(struct sii8620 *ctx, int ret) { static const char * const sink_str[] = { [SINK_NONE] = "NONE", @@ -410,23 +410,10 @@ static void sii8620_mr_devcap(struct sii8620 *ctx) [SINK_DVI] = "DVI" }; - u8 dcap[MHL_DCAP_SIZE]; char sink_name[20]; struct device *dev = ctx->dev; - sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, dcap, MHL_DCAP_SIZE); - if (ctx->error < 0) - return; - - dev_info(dev, "dcap: %*ph\n", MHL_DCAP_SIZE, dcap); - dev_info(dev, "detected dongle MHL %d.%d, ChipID %02x%02x:%02x%02x\n", - dcap[MHL_DCAP_MHL_VERSION] / 16, - dcap[MHL_DCAP_MHL_VERSION] % 16, dcap[MHL_DCAP_ADOPTER_ID_H], - dcap[MHL_DCAP_ADOPTER_ID_L], dcap[MHL_DCAP_DEVICE_ID_H], - dcap[MHL_DCAP_DEVICE_ID_L]); - sii8620_update_array(ctx->devcap, dcap, MHL_DCAP_SIZE); - - if (!(dcap[MHL_DCAP_CAT] & MHL_DCAP_CAT_SINK)) + if (ret < 0) return; sii8620_fetch_edid(ctx); @@ -449,6 +436,23 @@ static void sii8620_mr_devcap(struct sii8620 *ctx) sii8620_enable_hpd(ctx); } +static void sii8620_mr_devcap(struct sii8620 *ctx) +{ + u8 dcap[MHL_DCAP_SIZE]; + struct device *dev = ctx->dev; + + sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, dcap, MHL_DCAP_SIZE); + if (ctx->error < 0) + return; + + dev_info(dev, "detected dongle MHL %d.%d, ChipID %02x%02x:%02x%02x\n", + dcap[MHL_DCAP_MHL_VERSION] / 16, + dcap[MHL_DCAP_MHL_VERSION] % 16, + dcap[MHL_DCAP_ADOPTER_ID_H], dcap[MHL_DCAP_ADOPTER_ID_L], + dcap[MHL_DCAP_DEVICE_ID_H], dcap[MHL_DCAP_DEVICE_ID_L]); + sii8620_update_array(ctx->devcap, dcap, MHL_DCAP_SIZE); +} + static void sii8620_mr_xdevcap(struct sii8620 *ctx) { sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, ctx->xdevcap, @@ -1409,6 +1413,7 @@ static void sii8620_status_changed_path(struct sii8620 *ctx) | MHL_DST_LM_PATH_ENABLED); if (!sii8620_is_mhl3(ctx)) sii8620_mt_read_devcap(ctx, false); + sii8620_mt_set_cont(ctx, sii8620_sink_detected); } else { sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), MHL_DST_LM_CLK_MODE_NORMAL); From 26a4cef8155026488fe796cef56939fca909856f Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:41 +0100 Subject: [PATCH 29/47] drm/bridge/sii8620: set gen2 write burst before sending MSC command Write burst should be enabled for MHL_INT_RC_FEAT_REQ and disabled for other commands. The patch moves functions up and adds delay setting for MHL3 burst mode. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-15-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 88 ++++++++++++++++------------ 1 file changed, 50 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index d7a58c60b589..fde9e675e329 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -289,9 +289,59 @@ static void sii8620_mt_work(struct sii8620 *ctx) msg->send(ctx, msg); } +static void sii8620_enable_gen2_write_burst(struct sii8620 *ctx) +{ + u8 ctrl = BIT_MDT_RCV_CTRL_MDT_RCV_EN; + + if (ctx->gen2_write_burst) + return; + + if (ctx->mode >= CM_MHL1) + ctrl |= BIT_MDT_RCV_CTRL_MDT_DELAY_RCV_EN; + + sii8620_write_seq(ctx, + REG_MDT_RCV_TIMEOUT, 100, + REG_MDT_RCV_CTRL, ctrl + ); + ctx->gen2_write_burst = 1; +} + +static void sii8620_disable_gen2_write_burst(struct sii8620 *ctx) +{ + if (!ctx->gen2_write_burst) + return; + + sii8620_write_seq_static(ctx, + REG_MDT_XMIT_CTRL, 0, + REG_MDT_RCV_CTRL, 0 + ); + ctx->gen2_write_burst = 0; +} + +static void sii8620_start_gen2_write_burst(struct sii8620 *ctx) +{ + sii8620_write_seq_static(ctx, + REG_MDT_INT_1_MASK, BIT_MDT_RCV_TIMEOUT + | BIT_MDT_RCV_SM_ABORT_PKT_RCVD | BIT_MDT_RCV_SM_ERROR + | BIT_MDT_XMIT_TIMEOUT | BIT_MDT_XMIT_SM_ABORT_PKT_RCVD + | BIT_MDT_XMIT_SM_ERROR, + REG_MDT_INT_0_MASK, BIT_MDT_XFIFO_EMPTY + | BIT_MDT_IDLE_AFTER_HAWB_DISABLE + | BIT_MDT_RFIFO_DATA_RDY + ); + sii8620_enable_gen2_write_burst(ctx); +} + static void sii8620_mt_msc_cmd_send(struct sii8620 *ctx, struct sii8620_mt_msg *msg) { + if (msg->reg[0] == MHL_SET_INT && + msg->reg[1] == MHL_INT_REG(RCHANGE) && + msg->reg[2] == MHL_INT_RC_FEAT_REQ) + sii8620_enable_gen2_write_burst(ctx); + else + sii8620_disable_gen2_write_burst(ctx); + switch (msg->reg[0]) { case MHL_WRITE_STAT: case MHL_SET_INT: @@ -957,44 +1007,6 @@ static void sii8620_enable_hpd(struct sii8620 *ctx) ); } -static void sii8620_enable_gen2_write_burst(struct sii8620 *ctx) -{ - if (ctx->gen2_write_burst) - return; - - sii8620_write_seq_static(ctx, - REG_MDT_RCV_TIMEOUT, 100, - REG_MDT_RCV_CTRL, BIT_MDT_RCV_CTRL_MDT_RCV_EN - ); - ctx->gen2_write_burst = 1; -} - -static void sii8620_disable_gen2_write_burst(struct sii8620 *ctx) -{ - if (!ctx->gen2_write_burst) - return; - - sii8620_write_seq_static(ctx, - REG_MDT_XMIT_CTRL, 0, - REG_MDT_RCV_CTRL, 0 - ); - ctx->gen2_write_burst = 0; -} - -static void sii8620_start_gen2_write_burst(struct sii8620 *ctx) -{ - sii8620_write_seq_static(ctx, - REG_MDT_INT_1_MASK, BIT_MDT_RCV_TIMEOUT - | BIT_MDT_RCV_SM_ABORT_PKT_RCVD | BIT_MDT_RCV_SM_ERROR - | BIT_MDT_XMIT_TIMEOUT | BIT_MDT_XMIT_SM_ABORT_PKT_RCVD - | BIT_MDT_XMIT_SM_ERROR, - REG_MDT_INT_0_MASK, BIT_MDT_XFIFO_EMPTY - | BIT_MDT_IDLE_AFTER_HAWB_DISABLE - | BIT_MDT_RFIFO_DATA_RDY - ); - sii8620_enable_gen2_write_burst(ctx); -} - static void sii8620_mhl_discover(struct sii8620 *ctx) { sii8620_write_seq_static(ctx, From 6bed9bc2ae483c5e8a7743515b63653eaad829c6 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:42 +0100 Subject: [PATCH 30/47] drm/bridge/sii8620: do not stop MHL output when TMDS input is stopped Stopping output causes full re-detection of the sink and slows down the process. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-16-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index fde9e675e329..a27ce5493323 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -1615,17 +1615,6 @@ static void sii8620_scdt_high(struct sii8620 *ctx) ); } -static void sii8620_scdt_low(struct sii8620 *ctx) -{ - sii8620_write(ctx, REG_TMDS_CSTAT_P3, - BIT_TMDS_CSTAT_P3_SCDT_CLR_AVI_DIS | - BIT_TMDS_CSTAT_P3_CLR_AVI); - - sii8620_stop_video(ctx); - - sii8620_write(ctx, REG_INTR8_MASK, 0); -} - static void sii8620_irq_scdt(struct sii8620 *ctx) { u8 stat = sii8620_readb(ctx, REG_INTR5); @@ -1635,8 +1624,6 @@ static void sii8620_irq_scdt(struct sii8620 *ctx) if (cstat & BIT_TMDS_CSTAT_P3_SCDT) sii8620_scdt_high(ctx); - else - sii8620_scdt_low(ctx); } sii8620_write(ctx, REG_INTR5, stat); From 4dc3c0712720ca70b0563b4b64cf14175a96ef8c Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:43 +0100 Subject: [PATCH 31/47] drm/bridge/sii8620: add delay during cbus reset Without delay CBUS sometimes was not reset properly. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-17-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index a27ce5493323..1be9c89290c4 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -892,11 +892,10 @@ static void sii8620_hw_reset(struct sii8620 *ctx) static void sii8620_cbus_reset(struct sii8620 *ctx) { - sii8620_write_seq_static(ctx, - REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST - | BIT_PWD_SRST_CBUS_RST_SW_EN, - REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN - ); + sii8620_write(ctx, REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST + | BIT_PWD_SRST_CBUS_RST_SW_EN); + usleep_range(10000, 20000); + sii8620_write(ctx, REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN); } static void sii8620_set_auto_zone(struct sii8620 *ctx) From 263b5c93a2947bcab329b55e52b859e43a6539ee Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:44 +0100 Subject: [PATCH 32/47] drm/bridge/sii8620: split EDID read and write code MHL3 requires that after reading EDID from the sink source should ask peer for features. To make both protocols happy the patch splits the code accordingly. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-18-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 31 ++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 1be9c89290c4..3a37dfdf919e 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -482,6 +482,13 @@ static void sii8620_sink_detected(struct sii8620 *ctx, int ret) dev_info(dev, "detected sink(type: %s): %s\n", sink_str[ctx->sink_type], sink_name); +} + +static void sii8620_edid_read(struct sii8620 *ctx, int ret) +{ + if (ret < 0) + return; + sii8620_set_upstream_edid(ctx); sii8620_enable_hpd(ctx); } @@ -788,12 +795,12 @@ static void sii8620_fetch_edid(struct sii8620 *ctx) edid = new_edid; } } - - if (fetched + FETCH_SIZE == edid_len) - sii8620_write(ctx, REG_INTR3, int3); } - sii8620_write(ctx, REG_LM_DDC, lm_ddc); + sii8620_write_seq(ctx, + REG_INTR3_MASK, BIT_DDC_CMD_DONE, + REG_LM_DDC, lm_ddc + ); end: kfree(ctx->edid); @@ -1707,6 +1714,21 @@ static void sii8620_irq_block(struct sii8620 *ctx) sii8620_write(ctx, REG_EMSCINTR, stat); } +static void sii8620_irq_ddc(struct sii8620 *ctx) +{ + u8 stat = sii8620_readb(ctx, REG_INTR3); + + if (stat & BIT_DDC_CMD_DONE) { + sii8620_write(ctx, REG_INTR3_MASK, 0); + if (sii8620_is_mhl3(ctx)) + sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), + MHL_INT_RC_FEAT_REQ); + else + sii8620_edid_read(ctx, 0); + } + sii8620_write(ctx, REG_INTR3, stat); +} + /* endian agnostic, non-volatile version of test_bit */ static bool sii8620_test_bit(unsigned int nr, const u8 *addr) { @@ -1727,6 +1749,7 @@ static irqreturn_t sii8620_irq_thread(int irq, void *data) { BIT_FAST_INTR_STAT_MERR, sii8620_irq_merr }, { BIT_FAST_INTR_STAT_BLOCK, sii8620_irq_block }, { BIT_FAST_INTR_STAT_EDID, sii8620_irq_edid }, + { BIT_FAST_INTR_STAT_DDC, sii8620_irq_ddc }, { BIT_FAST_INTR_STAT_SCDT, sii8620_irq_scdt }, { BIT_FAST_INTR_STAT_INFR, sii8620_irq_infr }, }; From 003f9929f85270a74a34990a60263fa847408fd0 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:45 +0100 Subject: [PATCH 33/47] drm/bridge/sii8620: fix disconnect sequence The patch allows to avoid rare cases when discovery fails. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-19-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 3a37dfdf919e..80ff64322fa0 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -1287,10 +1287,12 @@ static void sii8620_disconnect(struct sii8620 *ctx) { sii8620_disable_gen2_write_burst(ctx); sii8620_stop_video(ctx); - msleep(50); + msleep(100); sii8620_cbus_reset(ctx); sii8620_set_mode(ctx, CM_DISCONNECTED); sii8620_write_seq_static(ctx, + REG_TX_ZONE_CTL1, 0, + REG_MHL_PLL_CTL0, 0x07, REG_COC_CTL0, 0x40, REG_CBUS3_CNVT, 0x84, REG_COC_CTL14, 0x00, From be1cd6fe205806fbbcf59061d7b64759b160ca31 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:46 +0100 Subject: [PATCH 34/47] drm/bridge/mhl: add MHL3 infoframe related definitions MHL3 protocol uses vendor specific infoframes to transmit additional information to the sink. This patch adds definitions of structures and constants used to create such frames. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-20-git-send-email-a.hajda@samsung.com --- include/drm/bridge/mhl.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/include/drm/bridge/mhl.h b/include/drm/bridge/mhl.h index e8d194da4b2c..fbdfc8d7f3c7 100644 --- a/include/drm/bridge/mhl.h +++ b/include/drm/bridge/mhl.h @@ -341,4 +341,36 @@ struct mhl_burst_audio_descr { u8 short_desc[9]; } __packed; +/* + * MHL3 infoframe related definitions + */ + +#define MHL3_IEEE_OUI 0x7ca61d +#define MHL3_INFOFRAME_SIZE 15 + +enum mhl3_video_format { + MHL3_VIDEO_FORMAT_NONE, + MHL3_VIDEO_FORMAT_3D, + MHL3_VIDEO_FORMAT_MULTI_VIEW, + MHL3_VIDEO_FORMAT_DUAL_3D +}; + +enum mhl3_3d_format_type { + MHL3_3D_FORMAT_TYPE_FS, /* frame sequential */ + MHL3_3D_FORMAT_TYPE_TB, /* top-bottom */ + MHL3_3D_FORMAT_TYPE_LR, /* left-right */ + MHL3_3D_FORMAT_TYPE_FS_TB, /* frame sequential, top-bottom */ + MHL3_3D_FORMAT_TYPE_FS_LR, /* frame sequential, left-right */ + MHL3_3D_FORMAT_TYPE_TB_LR /* top-bottom, left-right */ +}; + +struct mhl3_infoframe { + unsigned char version; + enum mhl3_video_format video_format; + enum mhl3_3d_format_type format_type; + bool sep_audio; + int hev_format; + int av_delay; +}; + #endif /* __MHL_H__ */ From bf1722cab55a32a7a18c0155034351084bac59fa Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:47 +0100 Subject: [PATCH 35/47] drm/bridge/sii8620: rewrite hdmi start sequence MHL3 protocol requires registry adjustments depending on chosen video mode. Necessary information is gathered in mode_fixup callback. In case of HDMI video modes driver should also send special AVI and MHL3 infoframes. The patch introduces generic helpers for handling MHL3 infoframes, in case of appearance of other users of MHL3 infoframes these function can be moved to common library. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-21-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 274 ++++++++++++++++++++++++--- drivers/gpu/drm/bridge/sil-sii8620.h | 15 +- 2 files changed, 259 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 80ff64322fa0..c8d06d6123c3 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -32,6 +32,8 @@ #define SII8620_BURST_BUF_LEN 288 #define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3) +#define MHL1_MAX_LCLK 225000 +#define MHL3_MAX_LCLK 600000 enum sii8620_mode { CM_DISCONNECTED, @@ -62,6 +64,9 @@ struct sii8620 { struct regulator_bulk_data supplies[2]; struct mutex lock; /* context lock, protects fields below */ int error; + int pixel_clock; + unsigned int use_packed_pixel:1; + int video_code; enum sii8620_mode mode; enum sii8620_sink_type sink_type; u8 cbus_status; @@ -69,7 +74,7 @@ struct sii8620 { u8 xstat[MHL_XDS_SIZE]; u8 devcap[MHL_DCAP_SIZE]; u8 xdevcap[MHL_XDC_SIZE]; - u8 avif[19]; + u8 avif[HDMI_INFOFRAME_SIZE(AVI)]; struct edid *edid; unsigned int gen2_write_burst:1; enum sii8620_mt_state mt_state; @@ -686,6 +691,40 @@ static void sii8620_burst_tx_rbuf_info(struct sii8620 *ctx, int size) d->size = cpu_to_le16(size); } +static u8 sii8620_checksum(void *ptr, int size) +{ + u8 *d = ptr, sum = 0; + + while (size--) + sum += *d++; + + return sum; +} + +static void sii8620_mhl_burst_hdr_set(struct mhl3_burst_header *h, + enum mhl_burst_id id) +{ + h->id = cpu_to_be16(id); + h->total_entries = 1; + h->sequence_index = 1; +} + +static void sii8620_burst_tx_bits_per_pixel_fmt(struct sii8620 *ctx, u8 fmt) +{ + struct mhl_burst_bits_per_pixel_fmt *d; + const int size = sizeof(*d) + sizeof(d->desc[0]); + + d = sii8620_burst_get_tx_buf(ctx, size); + if (!d) + return; + + sii8620_mhl_burst_hdr_set(&d->hdr, MHL_BURST_ID_BITS_PER_PIXEL_FMT); + d->num_entries = 1; + d->desc[0].stream_id = 0; + d->desc[0].pixel_format = fmt; + d->hdr.checksum -= sii8620_checksum(d, size); +} + static void sii8620_burst_rx_all(struct sii8620 *ctx) { u8 *d = ctx->burst.rx_buf; @@ -950,32 +989,193 @@ static void sii8620_stop_video(struct sii8620 *ctx) sii8620_write(ctx, REG_TPI_SC, val); } +static void sii8620_set_format(struct sii8620 *ctx) +{ + u8 out_fmt; + + if (sii8620_is_mhl3(ctx)) { + sii8620_setbits(ctx, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED, + ctx->use_packed_pixel ? ~0 : 0); + } else { + if (ctx->use_packed_pixel) + sii8620_write_seq_static(ctx, + REG_VID_MODE, BIT_VID_MODE_M1080P, + REG_MHL_TOP_CTL, BIT_MHL_TOP_CTL_MHL_PP_SEL | 1, + REG_MHLTX_CTL6, 0x60 + ); + else + sii8620_write_seq_static(ctx, + REG_VID_MODE, 0, + REG_MHL_TOP_CTL, 1, + REG_MHLTX_CTL6, 0xa0 + ); + } + + if (ctx->use_packed_pixel) + out_fmt = VAL_TPI_FORMAT(YCBCR422, FULL) | + BIT_TPI_OUTPUT_CSCMODE709; + else + out_fmt = VAL_TPI_FORMAT(RGB, FULL); + + sii8620_write_seq(ctx, + REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), + REG_TPI_OUTPUT, out_fmt, + ); +} + +static int mhl3_infoframe_init(struct mhl3_infoframe *frame) +{ + memset(frame, 0, sizeof(*frame)); + + frame->version = 3; + frame->hev_format = -1; + return 0; +} + +static ssize_t mhl3_infoframe_pack(struct mhl3_infoframe *frame, + void *buffer, size_t size) +{ + const int frm_len = HDMI_INFOFRAME_HEADER_SIZE + MHL3_INFOFRAME_SIZE; + u8 *ptr = buffer; + + if (size < frm_len) + return -ENOSPC; + + memset(buffer, 0, size); + ptr[0] = HDMI_INFOFRAME_TYPE_VENDOR; + ptr[1] = frame->version; + ptr[2] = MHL3_INFOFRAME_SIZE; + ptr[4] = MHL3_IEEE_OUI & 0xff; + ptr[5] = (MHL3_IEEE_OUI >> 8) & 0xff; + ptr[6] = (MHL3_IEEE_OUI >> 16) & 0xff; + ptr[7] = frame->video_format & 0x3; + ptr[7] |= (frame->format_type & 0x7) << 2; + ptr[7] |= frame->sep_audio ? BIT(5) : 0; + if (frame->hev_format >= 0) { + ptr[9] = 1; + ptr[10] = (frame->hev_format >> 8) & 0xff; + ptr[11] = frame->hev_format & 0xff; + } + if (frame->av_delay) { + bool sign = frame->av_delay < 0; + int delay = sign ? -frame->av_delay : frame->av_delay; + + ptr[12] = (delay >> 16) & 0xf; + if (sign) + ptr[12] |= BIT(4); + ptr[13] = (delay >> 8) & 0xff; + ptr[14] = delay & 0xff; + } + ptr[3] -= sii8620_checksum(buffer, frm_len); + return frm_len; +} + +static void sii8620_set_infoframes(struct sii8620 *ctx) +{ + struct mhl3_infoframe mhl_frm; + union hdmi_infoframe frm; + u8 buf[31]; + int ret; + + if (!sii8620_is_mhl3(ctx) || !ctx->use_packed_pixel) { + sii8620_write(ctx, REG_TPI_SC, + BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); + sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, ctx->avif + 3, + ARRAY_SIZE(ctx->avif) - 3); + sii8620_write(ctx, REG_PKT_FILTER_0, + BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT | + BIT_PKT_FILTER_0_DROP_MPEG_PKT | + BIT_PKT_FILTER_0_DROP_GCP_PKT, + BIT_PKT_FILTER_1_DROP_GEN_PKT); + return; + } + + ret = hdmi_avi_infoframe_init(&frm.avi); + frm.avi.colorspace = HDMI_COLORSPACE_YUV422; + frm.avi.active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; + frm.avi.picture_aspect = HDMI_PICTURE_ASPECT_16_9; + frm.avi.colorimetry = HDMI_COLORIMETRY_ITU_709; + frm.avi.video_code = ctx->video_code; + if (!ret) + ret = hdmi_avi_infoframe_pack(&frm.avi, buf, ARRAY_SIZE(buf)); + if (ret > 0) + sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, buf + 3, ret - 3); + sii8620_write(ctx, REG_PKT_FILTER_0, + BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT | + BIT_PKT_FILTER_0_DROP_MPEG_PKT | + BIT_PKT_FILTER_0_DROP_AVI_PKT | + BIT_PKT_FILTER_0_DROP_GCP_PKT, + BIT_PKT_FILTER_1_VSI_OVERRIDE_DIS | + BIT_PKT_FILTER_1_DROP_GEN_PKT | + BIT_PKT_FILTER_1_DROP_VSIF_PKT); + + sii8620_write(ctx, REG_TPI_INFO_FSEL, BIT_TPI_INFO_FSEL_EN + | BIT_TPI_INFO_FSEL_RPT | VAL_TPI_INFO_FSEL_VSI); + ret = mhl3_infoframe_init(&mhl_frm); + if (!ret) + ret = mhl3_infoframe_pack(&mhl_frm, buf, ARRAY_SIZE(buf)); + sii8620_write_buf(ctx, REG_TPI_INFO_B0, buf, ret); +} + static void sii8620_start_hdmi(struct sii8620 *ctx) { sii8620_write_seq_static(ctx, REG_RX_HDMI_CTRL2, VAL_RX_HDMI_CTRL2_DEFVAL | BIT_RX_HDMI_CTRL2_USE_AV_MUTE, REG_VID_OVRRD, BIT_VID_OVRRD_PP_AUTO_DISABLE - | BIT_VID_OVRRD_M1080P_OVRRD, - REG_VID_MODE, 0, - REG_MHL_TOP_CTL, 0x1, - REG_MHLTX_CTL6, 0xa0, - REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), - REG_TPI_OUTPUT, VAL_TPI_FORMAT(RGB, FULL), - ); + | BIT_VID_OVRRD_M1080P_OVRRD); + sii8620_set_format(ctx); - sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), - MHL_DST_LM_CLK_MODE_NORMAL | - MHL_DST_LM_PATH_ENABLED); + if (!sii8620_is_mhl3(ctx)) { + sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), + MHL_DST_LM_CLK_MODE_NORMAL | MHL_DST_LM_PATH_ENABLED); + sii8620_set_auto_zone(ctx); + } else { + static const struct { + int max_clk; + u8 zone; + u8 link_rate; + u8 rrp_decode; + } clk_spec[] = { + { 150000, VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS, + MHL_XDS_LINK_RATE_1_5_GBPS, 0x38 }, + { 300000, VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS, + MHL_XDS_LINK_RATE_3_0_GBPS, 0x40 }, + { 600000, VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS, + MHL_XDS_LINK_RATE_6_0_GBPS, 0x40 }, + }; + u8 p0_ctrl = BIT_M3_P0CTRL_MHL3_P0_PORT_EN; + int clk = ctx->pixel_clock * (ctx->use_packed_pixel ? 2 : 3); + int i; - sii8620_set_auto_zone(ctx); + for (i = 0; i < ARRAY_SIZE(clk_spec); ++i) + if (clk < clk_spec[i].max_clk) + break; - sii8620_write(ctx, REG_TPI_SC, BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); + if (100 * clk >= 98 * clk_spec[i].max_clk) + p0_ctrl |= BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN; - sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, ctx->avif, - ARRAY_SIZE(ctx->avif)); + sii8620_burst_tx_bits_per_pixel_fmt(ctx, ctx->use_packed_pixel); + sii8620_burst_send(ctx); + sii8620_write_seq(ctx, + REG_MHL_DP_CTL0, 0xf0, + REG_MHL3_TX_ZONE_CTL, clk_spec[i].zone); + sii8620_setbits(ctx, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PORT_EN + | BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN, p0_ctrl); + sii8620_setbits(ctx, REG_M3_POSTM, MSK_M3_POSTM_RRP_DECODE, + clk_spec[i].rrp_decode); + sii8620_write_seq_static(ctx, + REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE + | BIT_M3_CTRL_H2M_SWRST, + REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE + ); + sii8620_mt_write_stat(ctx, MHL_XDS_REG(AVLINK_MODE_CONTROL), + clk_spec[i].link_rate); + } - sii8620_write(ctx, REG_PKT_FILTER_0, 0xa1, 0x2); + sii8620_set_infoframes(ctx); } static void sii8620_start_video(struct sii8620 *ctx) @@ -1835,22 +2035,44 @@ static bool sii8620_mode_fixup(struct drm_bridge *bridge, struct drm_display_mode *adjusted_mode) { struct sii8620 *ctx = bridge_to_sii8620(bridge); - bool ret = false; - int max_clock = 74250; + int max_lclk; + bool ret = true; + + if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) + return false; mutex_lock(&ctx->lock); - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - goto out; + max_lclk = sii8620_is_mhl3(ctx) ? MHL3_MAX_LCLK : MHL1_MAX_LCLK; + if (max_lclk > 3 * adjusted_mode->clock) { + ctx->use_packed_pixel = 0; + goto end; + } + if ((ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL) && + max_lclk > 2 * adjusted_mode->clock) { + ctx->use_packed_pixel = 1; + goto end; + } + ret = false; +end: + if (ret) { + u8 vic = drm_match_cea_mode(adjusted_mode); - if (ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL) - max_clock = 300000; + if (!vic) { + union hdmi_infoframe frm; + u8 mhl_vic[] = { 0, 95, 94, 93, 98 }; - ret = mode->clock <= max_clock; - -out: + drm_hdmi_vendor_infoframe_from_display_mode( + &frm.vendor.hdmi, adjusted_mode); + vic = frm.vendor.hdmi.vic; + if (vic >= ARRAY_SIZE(mhl_vic)) + vic = 0; + vic = mhl_vic[vic]; + } + ctx->video_code = vic; + ctx->pixel_clock = adjusted_mode->clock; + } mutex_unlock(&ctx->lock); - return ret; } diff --git a/drivers/gpu/drm/bridge/sil-sii8620.h b/drivers/gpu/drm/bridge/sil-sii8620.h index 683213acc756..613943aaf90d 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.h +++ b/drivers/gpu/drm/bridge/sil-sii8620.h @@ -1084,10 +1084,17 @@ /* TPI Info Frame Select, default value: 0x00 */ #define REG_TPI_INFO_FSEL 0x06bf -#define BIT_TPI_INFO_FSEL_TPI_INFO_EN BIT(7) -#define BIT_TPI_INFO_FSEL_TPI_INFO_RPT BIT(6) -#define BIT_TPI_INFO_FSEL_TPI_INFO_READ_FLAG BIT(5) -#define MSK_TPI_INFO_FSEL_TPI_INFO_SEL 0x07 +#define BIT_TPI_INFO_FSEL_EN BIT(7) +#define BIT_TPI_INFO_FSEL_RPT BIT(6) +#define BIT_TPI_INFO_FSEL_READ_FLAG BIT(5) +#define MSK_TPI_INFO_FSEL_PKT 0x07 +#define VAL_TPI_INFO_FSEL_AVI 0x00 +#define VAL_TPI_INFO_FSEL_SPD 0x01 +#define VAL_TPI_INFO_FSEL_AUD 0x02 +#define VAL_TPI_INFO_FSEL_MPG 0x03 +#define VAL_TPI_INFO_FSEL_GEN 0x04 +#define VAL_TPI_INFO_FSEL_GEN2 0x05 +#define VAL_TPI_INFO_FSEL_VSI 0x06 /* TPI Info Byte #0, default value: 0x00 */ #define REG_TPI_INFO_B0 0x06c0 From 269ed8eec9c2e66a00e4c127e4c06bf87d518112 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:48 +0100 Subject: [PATCH 36/47] drm/bridge/sii8620: send EMSC features on request Device should report to the peer which features are really supported. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-22-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 27 +++++++++++++++++++++++---- drivers/gpu/drm/bridge/sil-sii8620.h | 16 ++++++++-------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index c8d06d6123c3..fca49f6105df 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -1676,6 +1676,25 @@ static void sii8620_got_ecbus_speed(struct sii8620 *ctx, int ret) sii8620_mt_set_cont(ctx, sii8620_ecbus_up); } +static void sii8620_mhl_burst_emsc_support_set(struct mhl_burst_emsc_support *d, + enum mhl_burst_id id) +{ + sii8620_mhl_burst_hdr_set(&d->hdr, MHL_BURST_ID_EMSC_SUPPORT); + d->num_entries = 1; + d->burst_id[0] = cpu_to_be16(id); +} + +static void sii8620_send_features(struct sii8620 *ctx) +{ + u8 buf[16]; + + sii8620_write(ctx, REG_MDT_XMIT_CTRL, BIT_MDT_XMIT_CTRL_EN + | BIT_MDT_XMIT_CTRL_FIXED_BURST_LEN); + sii8620_mhl_burst_emsc_support_set((void *)buf, + MHL_BURST_ID_HID_PAYLOAD); + sii8620_write_buf(ctx, REG_MDT_XMIT_WRITE_PORT, buf, ARRAY_SIZE(buf)); +} + static void sii8620_msc_mr_set_int(struct sii8620 *ctx) { u8 ints[MHL_INT_SIZE]; @@ -1696,10 +1715,10 @@ static void sii8620_msc_mr_set_int(struct sii8620 *ctx) break; } } - if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_REQ) { - sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), - MHL_INT_RC_FEAT_COMPLETE); - } + if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_REQ) + sii8620_send_features(ctx); + if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_COMPLETE) + sii8620_edid_read(ctx, 0); } static struct sii8620_mt_msg *sii8620_msc_msg_first(struct sii8620 *ctx) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.h b/drivers/gpu/drm/bridge/sil-sii8620.h index 613943aaf90d..312b07f38a09 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.h +++ b/drivers/gpu/drm/bridge/sil-sii8620.h @@ -1298,14 +1298,14 @@ /* MDT Transmit Control, default value: 0x70 */ #define REG_MDT_XMIT_CTRL 0x0588 -#define BIT_MDT_XMIT_CTRL_MDT_XMIT_EN BIT(7) -#define BIT_MDT_XMIT_CTRL_MDT_XMIT_CMD_MERGE_EN BIT(6) -#define BIT_MDT_XMIT_CTRL_MDT_XMIT_FIXED_BURST_LEN BIT(5) -#define BIT_MDT_XMIT_CTRL_MDT_XMIT_FIXED_AID BIT(4) -#define BIT_MDT_XMIT_CTRL_MDT_XMIT_SINGLE_RUN_EN BIT(3) -#define BIT_MDT_XMIT_CTRL_MDT_CLR_ABORT_WAIT BIT(2) -#define BIT_MDT_XMIT_CTRL_MDT_XFIFO_CLR_ALL BIT(1) -#define BIT_MDT_XMIT_CTRL_MDT_XFIFO_CLR_CUR BIT(0) +#define BIT_MDT_XMIT_CTRL_EN BIT(7) +#define BIT_MDT_XMIT_CTRL_CMD_MERGE_EN BIT(6) +#define BIT_MDT_XMIT_CTRL_FIXED_BURST_LEN BIT(5) +#define BIT_MDT_XMIT_CTRL_FIXED_AID BIT(4) +#define BIT_MDT_XMIT_CTRL_SINGLE_RUN_EN BIT(3) +#define BIT_MDT_XMIT_CTRL_CLR_ABORT_WAIT BIT(2) +#define BIT_MDT_XMIT_CTRL_XFIFO_CLR_ALL BIT(1) +#define BIT_MDT_XMIT_CTRL_XFIFO_CLR_CUR BIT(0) /* MDT Receive WRITE Port, default value: 0x00 */ #define REG_MDT_XMIT_WRITE_PORT 0x0589 From 581a923730b41e678579aa268d4155bf345859fd Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:49 +0100 Subject: [PATCH 37/47] drm/bridge/sii8620: improve gen2 write burst IRQ routine The patch adds code to report back feature complete IRQ, and code to read and drop burst writes from peer. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-23-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index fca49f6105df..a0c5499f898b 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -1605,12 +1605,31 @@ static void sii8620_irq_disc(struct sii8620 *ctx) sii8620_write(ctx, REG_CBUS_DISC_INTR0, stat); } +static void sii8620_read_burst(struct sii8620 *ctx) +{ + u8 buf[17]; + + sii8620_read_buf(ctx, REG_MDT_RCV_READ_PORT, buf, ARRAY_SIZE(buf)); + sii8620_write(ctx, REG_MDT_RCV_CTRL, BIT_MDT_RCV_CTRL_MDT_RCV_EN | + BIT_MDT_RCV_CTRL_MDT_DELAY_RCV_EN | + BIT_MDT_RCV_CTRL_MDT_RFIFO_CLR_CUR); + sii8620_readb(ctx, REG_MDT_RFIFO_STAT); +} + static void sii8620_irq_g2wb(struct sii8620 *ctx) { u8 stat = sii8620_readb(ctx, REG_MDT_INT_0); if (stat & BIT_MDT_IDLE_AFTER_HAWB_DISABLE) - dev_dbg(ctx->dev, "HAWB idle\n"); + if (sii8620_is_mhl3(ctx)) + sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), + MHL_INT_RC_FEAT_COMPLETE); + + if (stat & BIT_MDT_RFIFO_DATA_RDY) + sii8620_read_burst(ctx); + + if (stat & BIT_MDT_XFIFO_EMPTY) + sii8620_write(ctx, REG_MDT_XMIT_CTRL, 0); sii8620_write(ctx, REG_MDT_INT_0, stat); } From a21e658bfbcdbbac26e92a76aa22db507b439f7c Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:50 +0100 Subject: [PATCH 38/47] drm/bridge/sii8620: add HSIC initialization code In case of MHL3 HSIC should be initialized. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-24-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 38 ++++++++++++++++++++++++++++ drivers/gpu/drm/bridge/sil-sii8620.h | 10 +++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index a0c5499f898b..5dd800d2e303 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -489,12 +489,50 @@ static void sii8620_sink_detected(struct sii8620 *ctx, int ret) sink_str[ctx->sink_type], sink_name); } +static void sii8620_hsic_init(struct sii8620 *ctx) +{ + if (!sii8620_is_mhl3(ctx)) + return; + + sii8620_write(ctx, REG_FCGC, + BIT_FCGC_HSIC_HOSTMODE | BIT_FCGC_HSIC_ENABLE); + sii8620_setbits(ctx, REG_HRXCTRL3, + BIT_HRXCTRL3_HRX_STAY_RESET | BIT_HRXCTRL3_STATUS_EN, ~0); + sii8620_setbits(ctx, REG_TTXNUMB, MSK_TTXNUMB_TTX_NUMBPS, 4); + sii8620_setbits(ctx, REG_TRXCTRL, BIT_TRXCTRL_TRX_FROM_SE_COC, ~0); + sii8620_setbits(ctx, REG_HTXCTRL, BIT_HTXCTRL_HTX_DRVCONN1, 0); + sii8620_setbits(ctx, REG_KEEPER, MSK_KEEPER_MODE, VAL_KEEPER_MODE_HOST); + sii8620_write_seq_static(ctx, + REG_TDMLLCTL, 0, + REG_UTSRST, BIT_UTSRST_HRX_SRST | BIT_UTSRST_HTX_SRST | + BIT_UTSRST_KEEPER_SRST | BIT_UTSRST_FC_SRST, + REG_UTSRST, BIT_UTSRST_HRX_SRST | BIT_UTSRST_HTX_SRST, + REG_HRXINTL, 0xff, + REG_HRXINTH, 0xff, + REG_TTXINTL, 0xff, + REG_TTXINTH, 0xff, + REG_TRXINTL, 0xff, + REG_TRXINTH, 0xff, + REG_HTXINTL, 0xff, + REG_HTXINTH, 0xff, + REG_FCINTR0, 0xff, + REG_FCINTR1, 0xff, + REG_FCINTR2, 0xff, + REG_FCINTR3, 0xff, + REG_FCINTR4, 0xff, + REG_FCINTR5, 0xff, + REG_FCINTR6, 0xff, + REG_FCINTR7, 0xff + ); +} + static void sii8620_edid_read(struct sii8620 *ctx, int ret) { if (ret < 0) return; sii8620_set_upstream_edid(ctx); + sii8620_hsic_init(ctx); sii8620_enable_hpd(ctx); } diff --git a/drivers/gpu/drm/bridge/sil-sii8620.h b/drivers/gpu/drm/bridge/sil-sii8620.h index 312b07f38a09..51ab540cf092 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.h +++ b/drivers/gpu/drm/bridge/sil-sii8620.h @@ -353,7 +353,7 @@ #define REG_TTXNUMB 0x0116 #define MSK_TTXNUMB_TTX_AFFCTRL_3_0 0xf0 #define BIT_TTXNUMB_TTX_COM1_AT_SYNC_WAIT BIT(3) -#define MSK_TTXNUMB_TTX_NUMBPS_2_0 0x07 +#define MSK_TTXNUMB_TTX_NUMBPS 0x07 /* TDM TX NUMSPISYM, default value: 0x04 */ #define REG_TTXSPINUMS 0x0117 @@ -433,12 +433,14 @@ /* HSIC Keeper, default value: 0x00 */ #define REG_KEEPER 0x0181 -#define MSK_KEEPER_KEEPER_MODE_1_0 0x03 +#define MSK_KEEPER_MODE 0x03 +#define VAL_KEEPER_MODE_HOST 0 +#define VAL_KEEPER_MODE_DEVICE 2 /* HSIC Flow Control General, default value: 0x02 */ #define REG_FCGC 0x0183 -#define BIT_FCGC_HSIC_FC_HOSTMODE BIT(1) -#define BIT_FCGC_HSIC_FC_ENABLE BIT(0) +#define BIT_FCGC_HSIC_HOSTMODE BIT(1) +#define BIT_FCGC_HSIC_ENABLE BIT(0) /* HSIC Flow Control CTR13, default value: 0xfc */ #define REG_FCCTR13 0x0191 From 9fc6ade810c0ad4f72d77525444fbe925716e7fc Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:51 +0100 Subject: [PATCH 39/47] drm/bridge/sii8620: enable MHL3 mode if possible Since all sub-protocols of MHL3 are already supported MHL3 mode can be enabled. With this patch it is possible to use packed pixel modes and clocks up to 300MHz - 1920x1080@60Hz and 4K modes. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-25-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 5dd800d2e303..dae94899ac45 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -1404,8 +1404,7 @@ static void sii8620_mhl_init(struct sii8620 *ctx) ); sii8620_disable_gen2_write_burst(ctx); - /* currently MHL3 is not supported, so we force version to 0 */ - sii8620_mt_write_stat(ctx, MHL_DST_REG(VERSION), 0); + sii8620_mt_write_stat(ctx, MHL_DST_REG(VERSION), SII8620_MHL_VERSION); sii8620_mt_write_stat(ctx, MHL_DST_REG(CONNECTED_RDY), MHL_DST_CONN_DCAP_RDY | MHL_DST_CONN_XDEVCAPP_SUPP | MHL_DST_CONN_POW_STAT); @@ -1672,14 +1671,16 @@ static void sii8620_irq_g2wb(struct sii8620 *ctx) sii8620_write(ctx, REG_MDT_INT_0, stat); } -static void sii8620_status_changed_dcap(struct sii8620 *ctx) +static void sii8620_status_dcap_ready(struct sii8620 *ctx) { - if (ctx->stat[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY) { - sii8620_set_mode(ctx, CM_MHL1); - sii8620_peer_specific_init(ctx); - sii8620_write(ctx, REG_INTR9_MASK, BIT_INTR9_DEVCAP_DONE - | BIT_INTR9_EDID_DONE | BIT_INTR9_EDID_ERROR); - } + enum sii8620_mode mode; + + mode = ctx->stat[MHL_DST_VERSION] >= 0x30 ? CM_MHL3 : CM_MHL1; + if (mode > ctx->mode) + sii8620_set_mode(ctx, mode); + sii8620_peer_specific_init(ctx); + sii8620_write(ctx, REG_INTR9_MASK, BIT_INTR9_DEVCAP_DONE + | BIT_INTR9_EDID_DONE | BIT_INTR9_EDID_ERROR); } static void sii8620_status_changed_path(struct sii8620 *ctx) @@ -1707,8 +1708,8 @@ static void sii8620_msc_mr_write_stat(struct sii8620 *ctx) sii8620_update_array(ctx->stat, st, MHL_DST_SIZE); sii8620_update_array(ctx->xstat, xst, MHL_XDS_SIZE); - if (st[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY) - sii8620_status_changed_dcap(ctx); + if (ctx->stat[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY) + sii8620_status_dcap_ready(ctx); if (st[MHL_DST_LINK_MODE] & MHL_DST_LM_PATH_ENABLED) sii8620_status_changed_path(ctx); From 45d0ea86d235251305a0e2e63485b08b5caa79e7 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 1 Feb 2017 08:47:52 +0100 Subject: [PATCH 40/47] drm/bridge/sii8620: enable interlace modes Bug in DECON(CRTC) driver prevented interlace modes from proper work. Since DECON is fixed interlace modes can be enabled in MHL. Signed-off-by: Andrzej Hajda Signed-off-by: Archit Taneja Link: http://patchwork.freedesktop.org/patch/msgid/1485935272-17337-26-git-send-email-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index dae94899ac45..cdd0a9d44ba1 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -2115,9 +2115,6 @@ static bool sii8620_mode_fixup(struct drm_bridge *bridge, int max_lclk; bool ret = true; - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) - return false; - mutex_lock(&ctx->lock); max_lclk = sii8620_is_mhl3(ctx) ? MHL3_MAX_LCLK : MHL1_MAX_LCLK; From 3e6e2fd5531c6c028f444de8fcb255738c9c3f9b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 2 Feb 2017 10:47:43 +0100 Subject: [PATCH 41/47] drm: meson: rename module name to meson-drm The module is currently named "meson.ko" which can lead to some confusion, this patches renames it "meson-drm.ko" Signed-off-by: Neil Armstrong Acked-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1486028864-19622-2-git-send-email-narmstrong@baylibre.com --- drivers/gpu/drm/meson/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile index 2591978b8aad..92cf84530f49 100644 --- a/drivers/gpu/drm/meson/Makefile +++ b/drivers/gpu/drm/meson/Makefile @@ -1,4 +1,4 @@ -meson-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o -meson-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o +meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o +meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o -obj-$(CONFIG_DRM_MESON) += meson.o +obj-$(CONFIG_DRM_MESON) += meson-drm.o From 8aaacbc0e5c729baa1dbae26f77755c96a23096e Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 2 Feb 2017 10:47:44 +0100 Subject: [PATCH 42/47] drm: meson: rename driver name to meson-drm The platform driver name is currently "meson" which can lead to some confusion, this patch renames it to "meson-drm" and removes the owner attribute since is always added by __platform_driver_register called by the module_platform_driver() macro. Signed-off-by: Neil Armstrong Acked-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1486028864-19622-3-git-send-email-narmstrong@baylibre.com --- drivers/gpu/drm/meson/meson_drv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index ff1f6019b97b..380bde7fce16 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -329,8 +329,7 @@ static struct platform_driver meson_drm_platform_driver = { .probe = meson_drv_probe, .remove = meson_drv_remove, .driver = { - .owner = THIS_MODULE, - .name = DRIVER_NAME, + .name = "meson-drm", .of_match_table = dt_match, }, }; From f30c92576af4bb155d26429540aae95e0a256ce2 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 2 Feb 2017 09:36:32 +0000 Subject: [PATCH 43/47] drm: Provide a driver hook for drm_dev_release() Some state is coupled into the device lifetime outside of the load/unload timeframe and requires teardown during final unreference from drm_dev_release(). For example, dmabufs hold both a device and module reference and may live longer than expected (i.e. the current pattern of the driver tearing down its state and then releasing a reference to the drm device) and yet touch driver private state when destroyed. v2: Export drm_dev_fini() and move the responsibility for finalizing the drm_device and freeing it to the release callback. (If no callback is provided, the core will call drm_dev_fini() and kfree(dev) as before.) v3: Remember to add drm_dev_fini() to drm_drv.h v4: Tidy language for kerneldoc v5: Cross reference from drm_dev_init() to note that driver->release() allows for arbitrary embedding. v6: Refer to driver data rather than driver state, as state is now becoming associated with the struct drm_atomic_state and friends. Signed-off-by: Chris Wilson Cc: Laurent Pinchart Cc: Daniel Vetter Reviewed-by: Laurent Pinchart [danvet: Use the proper reference for struct members, which is &drm_driver.release.] Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170202093632.31017-1-chris@chris-wilson.co.uk --- drivers/gpu/drm/drm_drv.c | 65 +++++++++++++++++++++++++++------------ include/drm/drm_drv.h | 13 ++++++++ 2 files changed, 58 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 6cbd67f4fbc5..45ce224688ce 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -465,7 +465,10 @@ static void drm_fs_inode_free(struct inode *inode) * that do embed &struct drm_device it must be placed first in the overall * structure, and the overall structure must be allocated using kmalloc(): The * drm core's release function unconditionally calls kfree() on the @dev pointer - * when the final reference is released. + * when the final reference is released. To override this behaviour, and so + * allow embedding of the drm_device inside the driver's device struct at an + * arbitrary offset, you must supply a &drm_driver.release callback and control + * the finalization explicitly. * * RETURNS: * 0 on success, or error code on failure. @@ -552,6 +555,41 @@ err_free: } EXPORT_SYMBOL(drm_dev_init); +/** + * drm_dev_fini - Finalize a dead DRM device + * @dev: DRM device + * + * Finalize a dead DRM device. This is the converse to drm_dev_init() and + * frees up all data allocated by it. All driver private data should be + * finalized first. Note that this function does not free the @dev, that is + * left to the caller. + * + * The ref-count of @dev must be zero, and drm_dev_fini() should only be called + * from a &drm_driver.release callback. + */ +void drm_dev_fini(struct drm_device *dev) +{ + drm_vblank_cleanup(dev); + + if (drm_core_check_feature(dev, DRIVER_GEM)) + drm_gem_destroy(dev); + + drm_legacy_ctxbitmap_cleanup(dev); + drm_ht_remove(&dev->map_hash); + drm_fs_inode_free(dev->anon_inode); + + drm_minor_free(dev, DRM_MINOR_PRIMARY); + drm_minor_free(dev, DRM_MINOR_RENDER); + drm_minor_free(dev, DRM_MINOR_CONTROL); + + mutex_destroy(&dev->master_mutex); + mutex_destroy(&dev->ctxlist_mutex); + mutex_destroy(&dev->filelist_mutex); + mutex_destroy(&dev->struct_mutex); + kfree(dev->unique); +} +EXPORT_SYMBOL(drm_dev_fini); + /** * drm_dev_alloc - Allocate new DRM device * @driver: DRM driver to allocate device for @@ -598,25 +636,12 @@ static void drm_dev_release(struct kref *ref) { struct drm_device *dev = container_of(ref, struct drm_device, ref); - drm_vblank_cleanup(dev); - - if (drm_core_check_feature(dev, DRIVER_GEM)) - drm_gem_destroy(dev); - - drm_legacy_ctxbitmap_cleanup(dev); - drm_ht_remove(&dev->map_hash); - drm_fs_inode_free(dev->anon_inode); - - drm_minor_free(dev, DRM_MINOR_PRIMARY); - drm_minor_free(dev, DRM_MINOR_RENDER); - drm_minor_free(dev, DRM_MINOR_CONTROL); - - mutex_destroy(&dev->master_mutex); - mutex_destroy(&dev->ctxlist_mutex); - mutex_destroy(&dev->filelist_mutex); - mutex_destroy(&dev->struct_mutex); - kfree(dev->unique); - kfree(dev); + if (dev->driver->release) { + dev->driver->release(dev); + } else { + drm_dev_fini(dev); + kfree(dev); + } } /** diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 732e85652d1e..5699f42195fe 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -102,6 +102,17 @@ struct drm_driver { * */ void (*unload) (struct drm_device *); + + /** + * @release: + * + * Optional callback for destroying device data after the final + * reference is released, i.e. the device is being destroyed. Drivers + * using this callback are responsible for calling drm_dev_fini() + * to finalize the device and then freeing the struct themselves. + */ + void (*release) (struct drm_device *); + int (*set_busid)(struct drm_device *dev, struct drm_master *master); /** @@ -437,6 +448,8 @@ extern unsigned int drm_debug; int drm_dev_init(struct drm_device *dev, struct drm_driver *driver, struct device *parent); +void drm_dev_fini(struct drm_device *dev); + struct drm_device *drm_dev_alloc(struct drm_driver *driver, struct device *parent); int drm_dev_register(struct drm_device *dev, unsigned long flags); From e4563f6ba71792c77aeccb2092cc23149b44e642 Mon Sep 17 00:00:00 2001 From: Gabriel Krisman Bertazi Date: Thu, 2 Feb 2017 14:26:40 -0200 Subject: [PATCH 44/47] drm: Rely on mode_config data for fb_helper initialization Instead of receiving the num_crts as a parameter, we can read it directly from the mode_config structure. I audited the drivers that invoke this helper and I believe all of them initialize the mode_config struct accordingly, prior to calling the fb_helper. I used the following coccinelle hack to make this transformation, except for the function headers and comment updates. The first and second rules are split because I couldn't find a way to remove the unused temporary variables at the same time I removed the parameter. // @r@ expression A,B,D,E; identifier C; @@ ( - drm_fb_helper_init(A,B,C,D) + drm_fb_helper_init(A,B,D) | - drm_fbdev_cma_init_with_funcs(A,B,C,D,E) + drm_fbdev_cma_init_with_funcs(A,B,D,E) | - drm_fbdev_cma_init(A,B,C,D) + drm_fbdev_cma_init(A,B,D) ) @@ expression A,B,C,D,E; @@ ( - drm_fb_helper_init(A,B,C,D) + drm_fb_helper_init(A,B,D) | - drm_fbdev_cma_init_with_funcs(A,B,C,D,E) + drm_fbdev_cma_init_with_funcs(A,B,D,E) | - drm_fbdev_cma_init(A,B,C,D) + drm_fbdev_cma_init(A,B,D) ) @@ identifier r.C; type T; expression V; @@ - T C; <... when != C - C = V; ...> // Changes since v1: - Rebased on top of the tip of drm-misc-next. - Remove mention to sti since a proper fix got merged. Suggested-by: Daniel Vetter Signed-off-by: Gabriel Krisman Bertazi Reviewed-by: Eric Anholt Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170202162640.27261-1-krisman@collabora.co.uk --- drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c | 1 - drivers/gpu/drm/arc/arcpgu_drv.c | 3 +-- drivers/gpu/drm/arm/hdlcd_drv.c | 2 +- drivers/gpu/drm/arm/malidp_drv.c | 2 +- drivers/gpu/drm/armada/armada_fbdev.c | 2 +- drivers/gpu/drm/ast/ast_fb.c | 3 +-- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 1 - drivers/gpu/drm/bochs/bochs_fbdev.c | 3 +-- drivers/gpu/drm/cirrus/cirrus_fbdev.c | 2 +- drivers/gpu/drm/drm_fb_cma_helper.c | 15 +++++++-------- drivers/gpu/drm/drm_fb_helper.c | 10 +++++----- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 5 +---- drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c | 2 +- drivers/gpu/drm/gma500/framebuffer.c | 2 +- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c | 3 +-- drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 3 +-- drivers/gpu/drm/i915/intel_fbdev.c | 3 +-- drivers/gpu/drm/imx/imx-drm-core.c | 3 +-- drivers/gpu/drm/meson/meson_drv.c | 1 - drivers/gpu/drm/mgag200/mgag200_fb.c | 2 +- drivers/gpu/drm/msm/msm_fbdev.c | 3 +-- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 2 +- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 3 +-- drivers/gpu/drm/omapdrm/omap_fbdev.c | 3 +-- drivers/gpu/drm/qxl/qxl_fb.c | 1 - drivers/gpu/drm/radeon/radeon_fb.c | 1 - drivers/gpu/drm/rcar-du/rcar_du_kms.c | 2 +- drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 5 +---- drivers/gpu/drm/sti/sti_drv.c | 2 +- drivers/gpu/drm/sun4i/sun4i_framebuffer.c | 4 +--- drivers/gpu/drm/tegra/fb.c | 2 +- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 3 +-- drivers/gpu/drm/udl/udl_fb.c | 3 +-- drivers/gpu/drm/vc4/vc4_kms.c | 1 - drivers/gpu/drm/virtio/virtgpu_fb.c | 1 - drivers/gpu/drm/zte/zx_drm_drv.c | 2 +- include/drm/drm_fb_cma_helper.h | 7 +++---- include/drm/drm_fb_helper.h | 3 +-- 38 files changed, 43 insertions(+), 73 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c index 838943d0962e..36ce3cac81ba 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c @@ -374,7 +374,6 @@ int amdgpu_fbdev_init(struct amdgpu_device *adev) &amdgpu_fb_helper_funcs); ret = drm_fb_helper_init(adev->ddev, &rfbdev->helper, - adev->mode_info.num_crtc, AMDGPUFB_CONN_LIMIT); if (ret) { kfree(rfbdev); diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c index 0b6eaa49a1db..8d8344ed655e 100644 --- a/drivers/gpu/drm/arc/arcpgu_drv.c +++ b/drivers/gpu/drm/arc/arcpgu_drv.c @@ -135,8 +135,7 @@ static int arcpgu_load(struct drm_device *drm) drm_kms_helper_poll_init(drm); arcpgu->fbdev = drm_fbdev_cma_init(drm, 16, - drm->mode_config.num_crtc, - drm->mode_config.num_connector); + drm->mode_config.num_connector); if (IS_ERR(arcpgu->fbdev)) { ret = PTR_ERR(arcpgu->fbdev); arcpgu->fbdev = NULL; diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index a2e5b04cdee3..4ce4f970920b 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -349,7 +349,7 @@ static int hdlcd_drm_bind(struct device *dev) drm_mode_config_reset(drm); drm_kms_helper_poll_init(drm); - hdlcd->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, + hdlcd->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); if (IS_ERR(hdlcd->fbdev)) { diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 99fb0ab39191..8b0672d4aee9 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -457,7 +457,7 @@ static int malidp_bind(struct device *dev) drm_mode_config_reset(drm); - malidp->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, + malidp->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); if (IS_ERR(malidp->fbdev)) { diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index 78335100cbc3..0233e1dc33e1 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -137,7 +137,7 @@ int armada_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs); - ret = drm_fb_helper_init(dev, fbh, 1, 1); + ret = drm_fb_helper_init(dev, fbh, 1); if (ret) { DRM_ERROR("failed to initialize drm fb helper\n"); goto err_fb_helper; diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index b085140fae95..5d0ffab411a8 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -315,8 +315,7 @@ int ast_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs); - ret = drm_fb_helper_init(dev, &afbdev->helper, - 1, 1); + ret = drm_fb_helper_init(dev, &afbdev->helper, 1); if (ret) goto free; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 0bf32d6ac39b..427bdff425c2 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -647,7 +647,6 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) platform_set_drvdata(pdev, dev); dc->fbdev = drm_fbdev_cma_init(dev, 24, - dev->mode_config.num_crtc, dev->mode_config.num_connector); if (IS_ERR(dc->fbdev)) dc->fbdev = NULL; diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c index 0317c3df6a22..932a769637ef 100644 --- a/drivers/gpu/drm/bochs/bochs_fbdev.c +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c @@ -169,8 +169,7 @@ int bochs_fbdev_init(struct bochs_device *bochs) drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper, &bochs_fb_helper_funcs); - ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper, - 1, 1); + ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper, 1); if (ret) return ret; diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 79a5cd108245..4cc679278182 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -289,7 +289,7 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) &cirrus_fb_helper_funcs); ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper, - cdev->num_crtc, CIRRUSFB_CONN_LIMIT); + CIRRUSFB_CONN_LIMIT); if (ret) return ret; diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 0ef8b284a4b8..596fabf18c3e 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -489,15 +489,14 @@ static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { * drm_fbdev_cma_init_with_funcs() - Allocate and initializes a drm_fbdev_cma struct * @dev: DRM device * @preferred_bpp: Preferred bits per pixel for the device - * @num_crtc: Number of CRTCs * @max_conn_count: Maximum number of connectors * @funcs: fb helper functions, in particular a custom dirty() callback * * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR. */ struct drm_fbdev_cma *drm_fbdev_cma_init_with_funcs(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int num_crtc, - unsigned int max_conn_count, const struct drm_framebuffer_funcs *funcs) + unsigned int preferred_bpp, unsigned int max_conn_count, + const struct drm_framebuffer_funcs *funcs) { struct drm_fbdev_cma *fbdev_cma; struct drm_fb_helper *helper; @@ -514,7 +513,7 @@ struct drm_fbdev_cma *drm_fbdev_cma_init_with_funcs(struct drm_device *dev, drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs); - ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count); + ret = drm_fb_helper_init(dev, helper, max_conn_count); if (ret < 0) { dev_err(dev->dev, "Failed to initialize drm fb helper.\n"); goto err_free; @@ -554,11 +553,11 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_init_with_funcs); * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR. */ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int num_crtc, - unsigned int max_conn_count) + unsigned int preferred_bpp, unsigned int max_conn_count) { - return drm_fbdev_cma_init_with_funcs(dev, preferred_bpp, num_crtc, - max_conn_count, &drm_fb_cma_funcs); + return drm_fbdev_cma_init_with_funcs(dev, preferred_bpp, + max_conn_count, + &drm_fb_cma_funcs); } EXPORT_SYMBOL_GPL(drm_fbdev_cma_init); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index c7fafa175755..a30c645035de 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -712,7 +712,6 @@ EXPORT_SYMBOL(drm_fb_helper_prepare); * drm_fb_helper_init - initialize a drm_fb_helper structure * @dev: drm device * @fb_helper: driver-allocated fbdev helper structure to initialize - * @crtc_count: maximum number of crtcs to support in this fbdev emulation * @max_conn_count: max connector count * * This allocates the structures for the fbdev helper with the given limits. @@ -727,9 +726,10 @@ EXPORT_SYMBOL(drm_fb_helper_prepare); */ int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *fb_helper, - int crtc_count, int max_conn_count) + int max_conn_count) { struct drm_crtc *crtc; + struct drm_mode_config *config = &dev->mode_config; int i; if (!drm_fbdev_emulation) @@ -738,11 +738,11 @@ int drm_fb_helper_init(struct drm_device *dev, if (!max_conn_count) return -EINVAL; - fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); + fb_helper->crtc_info = kcalloc(config->num_crtc, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); if (!fb_helper->crtc_info) return -ENOMEM; - fb_helper->crtc_count = crtc_count; + fb_helper->crtc_count = config->num_crtc; fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); if (!fb_helper->connector_info) { kfree(fb_helper->crtc_info); @@ -751,7 +751,7 @@ int drm_fb_helper_init(struct drm_device *dev, fb_helper->connector_info_alloc_count = dev->mode_config.num_connector; fb_helper->connector_count = 0; - for (i = 0; i < crtc_count; i++) { + for (i = 0; i < fb_helper->crtc_count; i++) { fb_helper->crtc_info[i].mode_set.connectors = kcalloc(max_conn_count, sizeof(struct drm_connector *), diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index a7884bea42eb..bcdb2720b68e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -208,7 +208,6 @@ int exynos_drm_fbdev_init(struct drm_device *dev) struct exynos_drm_fbdev *fbdev; struct exynos_drm_private *private = dev->dev_private; struct drm_fb_helper *helper; - unsigned int num_crtc; int ret; if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) @@ -225,9 +224,7 @@ int exynos_drm_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs); - num_crtc = dev->mode_config.num_crtc; - - ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR); + ret = drm_fb_helper_init(dev, helper, MAX_CONNECTOR); if (ret < 0) { DRM_ERROR("failed to initialize drm fb helper.\n"); goto err_init; diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index 48705248f894..04173235f448 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -94,7 +94,7 @@ static int fsl_dcu_load(struct drm_device *dev, unsigned long flags) "Invalid legacyfb_depth. Defaulting to 24bpp\n"); legacyfb_depth = 24; } - fsl_dev->fbdev = drm_fbdev_cma_init(dev, legacyfb_depth, 1, 1); + fsl_dev->fbdev = drm_fbdev_cma_init(dev, legacyfb_depth, 1); if (IS_ERR(fsl_dev->fbdev)) { ret = PTR_ERR(fsl_dev->fbdev); fsl_dev->fbdev = NULL; diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index fd1488bf5189..da42d2e1d397 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -564,7 +564,7 @@ int psb_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs); ret = drm_fb_helper_init(dev, &fbdev->psb_fb_helper, - dev_priv->ops->crtcs, INTELFB_CONN_LIMIT); + INTELFB_CONN_LIMIT); if (ret) goto free; diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c index 16fe79053ee1..d7a4d9095b33 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c @@ -200,8 +200,7 @@ int hibmc_fbdev_init(struct hibmc_drm_private *priv) &hibmc_fbdev_helper_funcs); /* Now just one crtc and one channel */ - ret = drm_fb_helper_init(priv->dev, - &hifbdev->helper, 1, 1); + ret = drm_fb_helper_init(priv->dev, &hifbdev->helper, 1); if (ret) { DRM_ERROR("failed to initialize fb helper: %d\n", ret); return ret; diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 7df0e8535e41..7ec93aec7e88 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -59,8 +59,7 @@ static void kirin_fbdev_output_poll_changed(struct drm_device *dev) drm_fbdev_cma_hotplug_event(priv->fbdev); } else { priv->fbdev = drm_fbdev_cma_init(dev, 32, - dev->mode_config.num_crtc, - dev->mode_config.num_connector); + dev->mode_config.num_connector); if (IS_ERR(priv->fbdev)) priv->fbdev = NULL; } diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index e0d9e72cf3d1..1b8ba2e77539 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -713,8 +713,7 @@ int intel_fbdev_init(struct drm_device *dev) if (!intel_fbdev_init_bios(dev, ifbdev)) ifbdev->preferred_bpp = 32; - ret = drm_fb_helper_init(dev, &ifbdev->helper, - INTEL_INFO(dev_priv)->num_pipes, 4); + ret = drm_fb_helper_init(dev, &ifbdev->helper, 4); if (ret) { kfree(ifbdev); return ret; diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 33404295b447..5ae48836652e 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -389,8 +389,7 @@ static int imx_drm_bind(struct device *dev) dev_warn(dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); legacyfb_depth = 16; } - imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, - drm->mode_config.num_crtc, MAX_CRTC); + imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, MAX_CRTC); if (IS_ERR(imxdrm->fbhelper)) { ret = PTR_ERR(imxdrm->fbhelper); imxdrm->fbhelper = NULL; diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 380bde7fce16..6f2fd82ed483 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -279,7 +279,6 @@ static int meson_drv_probe(struct platform_device *pdev) drm->mode_config.funcs = &meson_mode_config_funcs; priv->fbdev = drm_fbdev_cma_init(drm, 32, - drm->mode_config.num_crtc, drm->mode_config.num_connector); if (IS_ERR(priv->fbdev)) { ret = PTR_ERR(priv->fbdev); diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 1a665e1671b8..a449bb91213a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -286,7 +286,7 @@ int mgag200_fbdev_init(struct mga_device *mdev) drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs); ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper, - mdev->num_crtc, MGAG200FB_CONN_LIMIT); + MGAG200FB_CONN_LIMIT); if (ret) goto err_fb_helper; diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index f8a587eac6b8..6b1b375653f7 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -201,8 +201,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs); - ret = drm_fb_helper_init(dev, helper, - priv->num_crtcs, priv->num_connectors); + ret = drm_fb_helper_init(dev, helper, priv->num_connectors); if (ret) { dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret); goto fail; diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 955441f71500..cdfbe0284635 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -218,7 +218,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) drm_kms_helper_poll_init(drm); - mxsfb->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, + mxsfb->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); if (IS_ERR(mxsfb->fbdev)) { mxsfb->fbdev = NULL; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 9de6abb65781..971c147a3984 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -507,8 +507,7 @@ nouveau_fbcon_init(struct drm_device *dev) drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs); - ret = drm_fb_helper_init(dev, &fbcon->helper, - dev->mode_config.num_crtc, 4); + ret = drm_fb_helper_init(dev, &fbcon->helper, 4); if (ret) goto free; diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 2a839956dae6..942c4d483008 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -263,8 +263,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs); - ret = drm_fb_helper_init(dev, helper, - priv->num_crtcs, priv->num_connectors); + ret = drm_fb_helper_init(dev, helper, priv->num_connectors); if (ret) { dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret); goto fail; diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index 53edfa498af5..d479b7a7abe4 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -399,7 +399,6 @@ int qxl_fbdev_init(struct qxl_device *qdev) &qxl_fb_helper_funcs); ret = drm_fb_helper_init(&qdev->ddev, &qfbdev->helper, - qxl_num_crtc, QXLFB_CONN_LIMIT); if (ret) goto free; diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 6c10a83f3362..2be4fe9c7217 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -366,7 +366,6 @@ int radeon_fbdev_init(struct radeon_device *rdev) &radeon_fb_helper_funcs); ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper, - rdev->num_crtc, RADEONFB_CONN_LIMIT); if (ret) goto free; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index b5d3f16cfa12..ff61f6032f2c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -662,7 +662,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) drm_kms_helper_poll_init(dev); if (dev->mode_config.num_connector) { - fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc, + fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_connector); if (IS_ERR(fbdev)) return PTR_ERR(fbdev); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c index 52d1fdf9f9da..70ad50dd594d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c @@ -129,19 +129,16 @@ int rockchip_drm_fbdev_init(struct drm_device *dev) { struct rockchip_drm_private *private = dev->dev_private; struct drm_fb_helper *helper; - unsigned int num_crtc; int ret; if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) return -EINVAL; - num_crtc = dev->mode_config.num_crtc; - helper = &private->fbdev_helper; drm_fb_helper_prepare(dev, helper, &rockchip_drm_fb_helper_funcs); - ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR); + ret = drm_fb_helper_init(dev, helper, ROCKCHIP_MAX_CONNECTOR); if (ret < 0) { dev_err(dev->dev, "Failed to initialize drm fb helper - %d.\n", ret); diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index acc056644cd0..788feed208d7 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -360,7 +360,7 @@ static int sti_bind(struct device *dev) private = ddev->dev_private; if (ddev->mode_config.num_connector) { - fbdev = drm_fbdev_cma_init(ddev, 32, ddev->mode_config.num_crtc, + fbdev = drm_fbdev_cma_init(ddev, 32, ddev->mode_config.num_connector); if (IS_ERR(fbdev)) { DRM_DEBUG_DRIVER("Warning: fails to create fbdev\n"); diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c index 8b6ce619ad81..2c3beff8b53e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c +++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c @@ -40,9 +40,7 @@ struct drm_fbdev_cma *sun4i_framebuffer_init(struct drm_device *drm) drm->mode_config.funcs = &sun4i_de_mode_config_funcs; - return drm_fbdev_cma_init(drm, 32, - drm->mode_config.num_crtc, - drm->mode_config.num_connector); + return drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); } void sun4i_framebuffer_free(struct drm_device *drm) diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index f896e2ff7d47..f142f6a4db25 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -309,7 +309,7 @@ static int tegra_fbdev_init(struct tegra_fbdev *fbdev, struct drm_device *drm = fbdev->base.dev; int err; - err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors); + err = drm_fb_helper_init(drm, &fbdev->base, max_connectors); if (err < 0) { dev_err(drm->dev, "failed to initialize DRM FB helper: %d\n", err); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 919294a735fe..372d86fbb093 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -403,8 +403,7 @@ static int tilcdc_init(struct drm_driver *ddrv, struct device *dev) drm_mode_config_reset(ddev); priv->fbdev = drm_fbdev_cma_init(ddev, bpp, - ddev->mode_config.num_crtc, - ddev->mode_config.num_connector); + ddev->mode_config.num_connector); if (IS_ERR(priv->fbdev)) { ret = PTR_ERR(priv->fbdev); goto init_failed; diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index b8dc06d68777..8e8d60e9a1a2 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -441,8 +441,7 @@ int udl_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs); - ret = drm_fb_helper_init(dev, &ufbdev->helper, - 1, 1); + ret = drm_fb_helper_init(dev, &ufbdev->helper, 1); if (ret) goto free; diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index be8dd8262f27..ad7925a9e0ea 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -231,7 +231,6 @@ int vc4_kms_load(struct drm_device *dev) drm_mode_config_reset(dev); vc4->fbdev = drm_fbdev_cma_init(dev, 32, - dev->mode_config.num_crtc, dev->mode_config.num_connector); if (IS_ERR(vc4->fbdev)) vc4->fbdev = NULL; diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c index 24f99fc9d8a4..163a67db8cf1 100644 --- a/drivers/gpu/drm/virtio/virtgpu_fb.c +++ b/drivers/gpu/drm/virtio/virtgpu_fb.c @@ -387,7 +387,6 @@ int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev) drm_fb_helper_prepare(vgdev->ddev, &vgfbdev->helper, &virtio_gpu_fb_helper_funcs); ret = drm_fb_helper_init(vgdev->ddev, &vgfbdev->helper, - vgdev->num_scanouts, VIRTIO_GPUFB_CONN_LIMIT); if (ret) { kfree(vgfbdev); diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c index 13081fed902d..5c6944a1e72c 100644 --- a/drivers/gpu/drm/zte/zx_drm_drv.c +++ b/drivers/gpu/drm/zte/zx_drm_drv.c @@ -141,7 +141,7 @@ static int zx_drm_bind(struct device *dev) drm_mode_config_reset(drm); drm_kms_helper_poll_init(drm); - priv->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, + priv->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); if (IS_ERR(priv->fbdev)) { ret = PTR_ERR(priv->fbdev); diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h index 8dd6e5585e51..a5ecc0a58260 100644 --- a/include/drm/drm_fb_cma_helper.h +++ b/include/drm/drm_fb_cma_helper.h @@ -16,11 +16,10 @@ struct drm_plane; struct drm_plane_state; struct drm_fbdev_cma *drm_fbdev_cma_init_with_funcs(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int num_crtc, - unsigned int max_conn_count, const struct drm_framebuffer_funcs *funcs); + unsigned int preferred_bpp, unsigned int max_conn_count, + const struct drm_framebuffer_funcs *funcs); struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int num_crtc, - unsigned int max_conn_count); + unsigned int preferred_bpp, unsigned int max_conn_count); void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma); void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma); diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index e62e1cf22678..278820ee17ad 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -236,8 +236,7 @@ struct drm_fb_helper { void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, const struct drm_fb_helper_funcs *funcs); int drm_fb_helper_init(struct drm_device *dev, - struct drm_fb_helper *helper, int crtc_count, - int max_conn); + struct drm_fb_helper *helper, int max_conn); void drm_fb_helper_fini(struct drm_fb_helper *helper); int drm_fb_helper_blank(int blank, struct fb_info *info); int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, From 7e6328fd1f84f366b4cc17dc757025f485139f1a Mon Sep 17 00:00:00 2001 From: Gabriel Krisman Bertazi Date: Thu, 2 Feb 2017 17:39:00 -0200 Subject: [PATCH 45/47] drm: Fix build when FBDEV_EMULATION is disabled Commit be7f735cd5ea ("drm: Rely on mode_config data for fb_helper initialization") broke the build when CONFIG_DRM_FBDEV_EMULATION is disabled because it didn't update the prototype for drm_fb_helper_init in that case. Fixes: be7f735cd5ea ("drm: Rely on mode_config data for fb_helper initialization") Signed-off-by: Gabriel Krisman Bertazi Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170202193900.22075-1-krisman@collabora.co.uk --- include/drm/drm_fb_helper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 278820ee17ad..6f5acebb266a 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -307,7 +307,7 @@ static inline void drm_fb_helper_prepare(struct drm_device *dev, } static inline int drm_fb_helper_init(struct drm_device *dev, - struct drm_fb_helper *helper, int crtc_count, + struct drm_fb_helper *helper, int max_conn) { return 0; From 4e64e5539d152e202ad6eea2b6f65f3ab58d9428 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 2 Feb 2017 21:04:38 +0000 Subject: [PATCH 46/47] drm: Improve drm_mm search (and fix topdown allocation) with rbtrees MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_mm range manager claimed to support top-down insertion, but it was neither searching for the top-most hole that could fit the allocation request nor fitting the request to the hole correctly. In order to search the range efficiently, we create a secondary index for the holes using either their size or their address. This index allows us to find the smallest hole or the hole at the bottom or top of the range efficiently, whilst keeping the hole stack to rapidly service evictions. v2: Search for holes both high and low. Rename flags to mode. v3: Discover rb_entry_safe() and use it! v4: Kerneldoc for enum drm_mm_insert_mode. Signed-off-by: Chris Wilson Reviewed-by: Joonas Lahtinen Cc: Alex Deucher Cc: "Christian König" Cc: David Airlie Cc: Russell King Cc: Daniel Vetter Cc: Jani Nikula Cc: Sean Paul Cc: Lucas Stach Cc: Christian Gmeiner Cc: Rob Clark Cc: Thierry Reding Cc: Stephen Warren Cc: Alexandre Courbot Cc: Eric Anholt Cc: Sinclair Yeh Cc: Thomas Hellstrom Reviewed-by: Alex Deucher Reviewed-by: Sinclair Yeh # vmwgfx Reviewed-by: Lucas Stach #etnaviv Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170202210438.28702-1-chris@chris-wilson.co.uk --- drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c | 16 +- drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c | 20 +- drivers/gpu/drm/armada/armada_gem.c | 4 +- drivers/gpu/drm/drm_mm.c | 488 +++++++++++-------- drivers/gpu/drm/drm_vma_manager.c | 3 +- drivers/gpu/drm/etnaviv/etnaviv_mmu.c | 11 +- drivers/gpu/drm/i915/i915_gem.c | 10 +- drivers/gpu/drm/i915/i915_gem_evict.c | 9 +- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 5 +- drivers/gpu/drm/i915/i915_gem_gtt.c | 39 +- drivers/gpu/drm/i915/i915_gem_stolen.c | 6 +- drivers/gpu/drm/msm/msm_gem.c | 3 +- drivers/gpu/drm/msm/msm_gem_vma.c | 3 +- drivers/gpu/drm/selftests/test-drm_mm.c | 58 +-- drivers/gpu/drm/sis/sis_mm.c | 6 +- drivers/gpu/drm/tegra/gem.c | 4 +- drivers/gpu/drm/ttm/ttm_bo_manager.c | 18 +- drivers/gpu/drm/vc4/vc4_crtc.c | 2 +- drivers/gpu/drm/vc4/vc4_hvs.c | 3 +- drivers/gpu/drm/vc4/vc4_plane.c | 6 +- drivers/gpu/drm/via/via_mm.c | 4 +- drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c | 10 +- include/drm/drm_mm.h | 184 +++---- 23 files changed, 470 insertions(+), 442 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c index e4eb6dd3798a..0335c2f331e9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c @@ -97,8 +97,7 @@ int amdgpu_gtt_mgr_alloc(struct ttm_mem_type_manager *man, { struct amdgpu_gtt_mgr *mgr = man->priv; struct drm_mm_node *node = mem->mm_node; - enum drm_mm_search_flags sflags = DRM_MM_SEARCH_BEST; - enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT; + enum drm_mm_insert_mode mode; unsigned long fpfn, lpfn; int r; @@ -115,15 +114,14 @@ int amdgpu_gtt_mgr_alloc(struct ttm_mem_type_manager *man, else lpfn = man->size; - if (place && place->flags & TTM_PL_FLAG_TOPDOWN) { - sflags = DRM_MM_SEARCH_BELOW; - aflags = DRM_MM_CREATE_TOP; - } + mode = DRM_MM_INSERT_BEST; + if (place && place->flags & TTM_PL_FLAG_TOPDOWN) + mode = DRM_MM_INSERT_HIGH; spin_lock(&mgr->lock); - r = drm_mm_insert_node_in_range_generic(&mgr->mm, node, mem->num_pages, - mem->page_alignment, 0, - fpfn, lpfn, sflags, aflags); + r = drm_mm_insert_node_in_range(&mgr->mm, node, + mem->num_pages, mem->page_alignment, 0, + fpfn, lpfn, mode); spin_unlock(&mgr->lock); if (!r) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index ac9007986c11..9e577e3d3147 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -97,8 +97,7 @@ static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man, struct amdgpu_vram_mgr *mgr = man->priv; struct drm_mm *mm = &mgr->mm; struct drm_mm_node *nodes; - enum drm_mm_search_flags sflags = DRM_MM_SEARCH_DEFAULT; - enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT; + enum drm_mm_insert_mode mode; unsigned long lpfn, num_nodes, pages_per_node, pages_left; unsigned i; int r; @@ -121,10 +120,9 @@ static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man, if (!nodes) return -ENOMEM; - if (place->flags & TTM_PL_FLAG_TOPDOWN) { - sflags = DRM_MM_SEARCH_BELOW; - aflags = DRM_MM_CREATE_TOP; - } + mode = DRM_MM_INSERT_BEST; + if (place->flags & TTM_PL_FLAG_TOPDOWN) + mode = DRM_MM_INSERT_HIGH; pages_left = mem->num_pages; @@ -135,13 +133,11 @@ static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man, if (pages == pages_per_node) alignment = pages_per_node; - else - sflags |= DRM_MM_SEARCH_BEST; - r = drm_mm_insert_node_in_range_generic(mm, &nodes[i], pages, - alignment, 0, - place->fpfn, lpfn, - sflags, aflags); + r = drm_mm_insert_node_in_range(mm, &nodes[i], + pages, alignment, 0, + place->fpfn, lpfn, + mode); if (unlikely(r)) goto error; diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c index a293c8be232c..560d416deab2 100644 --- a/drivers/gpu/drm/armada/armada_gem.c +++ b/drivers/gpu/drm/armada/armada_gem.c @@ -148,8 +148,8 @@ armada_gem_linear_back(struct drm_device *dev, struct armada_gem_object *obj) return -ENOSPC; mutex_lock(&priv->linear_lock); - ret = drm_mm_insert_node(&priv->linear, node, size, align, - DRM_MM_SEARCH_DEFAULT); + ret = drm_mm_insert_node_generic(&priv->linear, node, + size, align, 0, 0); mutex_unlock(&priv->linear_lock); if (ret) { kfree(node); diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index e51876e588d6..8bfb0b327267 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -97,14 +97,6 @@ * locking would be fully redundant. */ -static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, - u64 size, - u64 alignment, - unsigned long color, - u64 start, - u64 end, - enum drm_mm_search_flags flags); - #ifdef CONFIG_DRM_DEBUG_MM #include @@ -226,69 +218,151 @@ static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, &drm_mm_interval_tree_augment); } -static void drm_mm_insert_helper(struct drm_mm_node *hole_node, - struct drm_mm_node *node, - u64 size, u64 alignment, - unsigned long color, - u64 range_start, u64 range_end, - enum drm_mm_allocator_flags flags) +#define RB_INSERT(root, member, expr) do { \ + struct rb_node **link = &root.rb_node, *rb = NULL; \ + u64 x = expr(node); \ + while (*link) { \ + rb = *link; \ + if (x < expr(rb_entry(rb, struct drm_mm_node, member))) \ + link = &rb->rb_left; \ + else \ + link = &rb->rb_right; \ + } \ + rb_link_node(&node->member, rb, link); \ + rb_insert_color(&node->member, &root); \ +} while (0) + +#define HOLE_SIZE(NODE) ((NODE)->hole_size) +#define HOLE_ADDR(NODE) (__drm_mm_hole_node_start(NODE)) + +static void add_hole(struct drm_mm_node *node) { - struct drm_mm *mm = hole_node->mm; - u64 hole_start = drm_mm_hole_node_start(hole_node); - u64 hole_end = drm_mm_hole_node_end(hole_node); - u64 adj_start = hole_start; - u64 adj_end = hole_end; + struct drm_mm *mm = node->mm; - DRM_MM_BUG_ON(!drm_mm_hole_follows(hole_node) || node->allocated); + node->hole_size = + __drm_mm_hole_node_end(node) - __drm_mm_hole_node_start(node); + DRM_MM_BUG_ON(!drm_mm_hole_follows(node)); - if (mm->color_adjust) - mm->color_adjust(hole_node, color, &adj_start, &adj_end); + RB_INSERT(mm->holes_size, rb_hole_size, HOLE_SIZE); + RB_INSERT(mm->holes_addr, rb_hole_addr, HOLE_ADDR); - adj_start = max(adj_start, range_start); - adj_end = min(adj_end, range_end); + list_add(&node->hole_stack, &mm->hole_stack); +} - if (flags & DRM_MM_CREATE_TOP) - adj_start = adj_end - size; +static void rm_hole(struct drm_mm_node *node) +{ + DRM_MM_BUG_ON(!drm_mm_hole_follows(node)); - if (alignment) { - u64 rem; + list_del(&node->hole_stack); + rb_erase(&node->rb_hole_size, &node->mm->holes_size); + rb_erase(&node->rb_hole_addr, &node->mm->holes_addr); + node->hole_size = 0; - div64_u64_rem(adj_start, alignment, &rem); - if (rem) { - if (flags & DRM_MM_CREATE_TOP) - adj_start -= rem; - else - adj_start += alignment - rem; + DRM_MM_BUG_ON(drm_mm_hole_follows(node)); +} + +static inline struct drm_mm_node *rb_hole_size_to_node(struct rb_node *rb) +{ + return rb_entry_safe(rb, struct drm_mm_node, rb_hole_size); +} + +static inline struct drm_mm_node *rb_hole_addr_to_node(struct rb_node *rb) +{ + return rb_entry_safe(rb, struct drm_mm_node, rb_hole_addr); +} + +static inline u64 rb_hole_size(struct rb_node *rb) +{ + return rb_entry(rb, struct drm_mm_node, rb_hole_size)->hole_size; +} + +static struct drm_mm_node *best_hole(struct drm_mm *mm, u64 size) +{ + struct rb_node *best = NULL; + struct rb_node **link = &mm->holes_size.rb_node; + + while (*link) { + struct rb_node *rb = *link; + + if (size <= rb_hole_size(rb)) { + link = &rb->rb_left; + best = rb; + } else { + link = &rb->rb_right; } } - if (adj_start == hole_start) { - hole_node->hole_follows = 0; - list_del(&hole_node->hole_stack); + return rb_hole_size_to_node(best); +} + +static struct drm_mm_node *find_hole(struct drm_mm *mm, u64 addr) +{ + struct drm_mm_node *node = NULL; + struct rb_node **link = &mm->holes_addr.rb_node; + + while (*link) { + u64 hole_start; + + node = rb_hole_addr_to_node(*link); + hole_start = __drm_mm_hole_node_start(node); + + if (addr < hole_start) + link = &node->rb_hole_addr.rb_left; + else if (addr > hole_start + node->hole_size) + link = &node->rb_hole_addr.rb_right; + else + break; } - node->start = adj_start; - node->size = size; - node->mm = mm; - node->color = color; - node->allocated = 1; + return node; +} - list_add(&node->node_list, &hole_node->node_list); +static struct drm_mm_node * +first_hole(struct drm_mm *mm, + u64 start, u64 end, u64 size, + enum drm_mm_insert_mode mode) +{ + if (RB_EMPTY_ROOT(&mm->holes_size)) + return NULL; - drm_mm_interval_tree_add_node(hole_node, node); + switch (mode) { + default: + case DRM_MM_INSERT_BEST: + return best_hole(mm, size); - DRM_MM_BUG_ON(node->start < range_start); - DRM_MM_BUG_ON(node->start < adj_start); - DRM_MM_BUG_ON(node->start + node->size > adj_end); - DRM_MM_BUG_ON(node->start + node->size > range_end); + case DRM_MM_INSERT_LOW: + return find_hole(mm, start); - node->hole_follows = 0; - if (__drm_mm_hole_node_start(node) < hole_end) { - list_add(&node->hole_stack, &mm->hole_stack); - node->hole_follows = 1; + case DRM_MM_INSERT_HIGH: + return find_hole(mm, end); + + case DRM_MM_INSERT_EVICT: + return list_first_entry_or_null(&mm->hole_stack, + struct drm_mm_node, + hole_stack); } +} - save_stack(node); +static struct drm_mm_node * +next_hole(struct drm_mm *mm, + struct drm_mm_node *node, + enum drm_mm_insert_mode mode) +{ + switch (mode) { + default: + case DRM_MM_INSERT_BEST: + return rb_hole_size_to_node(rb_next(&node->rb_hole_size)); + + case DRM_MM_INSERT_LOW: + return rb_hole_addr_to_node(rb_next(&node->rb_hole_addr)); + + case DRM_MM_INSERT_HIGH: + return rb_hole_addr_to_node(rb_prev(&node->rb_hole_addr)); + + case DRM_MM_INSERT_EVICT: + node = list_next_entry(node, hole_stack); + return &node->hole_stack == &mm->hole_stack ? NULL : node; + } } /** @@ -317,21 +391,12 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) return -ENOSPC; /* Find the relevant hole to add our node to */ - hole = drm_mm_interval_tree_iter_first(&mm->interval_tree, - node->start, ~(u64)0); - if (hole) { - if (hole->start < end) - return -ENOSPC; - } else { - hole = list_entry(drm_mm_nodes(mm), typeof(*hole), node_list); - } - - hole = list_last_entry(&hole->node_list, typeof(*hole), node_list); - if (!drm_mm_hole_follows(hole)) + hole = find_hole(mm, node->start); + if (!hole) return -ENOSPC; adj_start = hole_start = __drm_mm_hole_node_start(hole); - adj_end = hole_end = __drm_mm_hole_node_end(hole); + adj_end = hole_end = hole_start + hole->hole_size; if (mm->color_adjust) mm->color_adjust(hole, node->color, &adj_start, &adj_end); @@ -340,70 +405,130 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) return -ENOSPC; node->mm = mm; - node->allocated = 1; list_add(&node->node_list, &hole->node_list); - drm_mm_interval_tree_add_node(hole, node); + node->allocated = true; + node->hole_size = 0; - if (node->start == hole_start) { - hole->hole_follows = 0; - list_del(&hole->hole_stack); - } - - node->hole_follows = 0; - if (end != hole_end) { - list_add(&node->hole_stack, &mm->hole_stack); - node->hole_follows = 1; - } + rm_hole(hole); + if (node->start > hole_start) + add_hole(hole); + if (end < hole_end) + add_hole(node); save_stack(node); - return 0; } EXPORT_SYMBOL(drm_mm_reserve_node); /** - * drm_mm_insert_node_in_range_generic - ranged search for space and insert @node + * drm_mm_insert_node_in_range - ranged search for space and insert @node * @mm: drm_mm to allocate from * @node: preallocate node to insert * @size: size of the allocation * @alignment: alignment of the allocation * @color: opaque tag value to use for this node - * @start: start of the allowed range for this node - * @end: end of the allowed range for this node - * @sflags: flags to fine-tune the allocation search - * @aflags: flags to fine-tune the allocation behavior + * @range_start: start of the allowed range for this node + * @range_end: end of the allowed range for this node + * @mode: fine-tune the allocation search and placement * * The preallocated @node must be cleared to 0. * * Returns: * 0 on success, -ENOSPC if there's no suitable hole. */ -int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node, - u64 size, u64 alignment, - unsigned long color, - u64 start, u64 end, - enum drm_mm_search_flags sflags, - enum drm_mm_allocator_flags aflags) +int drm_mm_insert_node_in_range(struct drm_mm * const mm, + struct drm_mm_node * const node, + u64 size, u64 alignment, + unsigned long color, + u64 range_start, u64 range_end, + enum drm_mm_insert_mode mode) { - struct drm_mm_node *hole_node; + struct drm_mm_node *hole; + u64 remainder_mask; - if (WARN_ON(size == 0)) - return -EINVAL; + DRM_MM_BUG_ON(range_start >= range_end); - hole_node = drm_mm_search_free_in_range_generic(mm, - size, alignment, color, - start, end, sflags); - if (!hole_node) + if (unlikely(size == 0 || range_end - range_start < size)) return -ENOSPC; - drm_mm_insert_helper(hole_node, node, - size, alignment, color, - start, end, aflags); - return 0; + if (alignment <= 1) + alignment = 0; + + remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0; + for (hole = first_hole(mm, range_start, range_end, size, mode); hole; + hole = next_hole(mm, hole, mode)) { + u64 hole_start = __drm_mm_hole_node_start(hole); + u64 hole_end = hole_start + hole->hole_size; + u64 adj_start, adj_end; + u64 col_start, col_end; + + if (mode == DRM_MM_INSERT_LOW && hole_start >= range_end) + break; + + if (mode == DRM_MM_INSERT_HIGH && hole_end <= range_start) + break; + + col_start = hole_start; + col_end = hole_end; + if (mm->color_adjust) + mm->color_adjust(hole, color, &col_start, &col_end); + + adj_start = max(col_start, range_start); + adj_end = min(col_end, range_end); + + if (adj_end <= adj_start || adj_end - adj_start < size) + continue; + + if (mode == DRM_MM_INSERT_HIGH) + adj_start = adj_end - size; + + if (alignment) { + u64 rem; + + if (likely(remainder_mask)) + rem = adj_start & remainder_mask; + else + div64_u64_rem(adj_start, alignment, &rem); + if (rem) { + adj_start -= rem; + if (mode != DRM_MM_INSERT_HIGH) + adj_start += alignment; + + if (adj_start < max(col_start, range_start) || + min(col_end, range_end) - adj_start < size) + continue; + + if (adj_end <= adj_start || + adj_end - adj_start < size) + continue; + } + } + + node->mm = mm; + node->size = size; + node->start = adj_start; + node->color = color; + node->hole_size = 0; + + list_add(&node->node_list, &hole->node_list); + drm_mm_interval_tree_add_node(hole, node); + node->allocated = true; + + rm_hole(hole); + if (adj_start > hole_start) + add_hole(hole); + if (adj_start + size < hole_end) + add_hole(node); + + save_stack(node); + return 0; + } + + return -ENOSPC; } -EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic); +EXPORT_SYMBOL(drm_mm_insert_node_in_range); /** * drm_mm_remove_node - Remove a memory node from the allocator. @@ -421,93 +546,21 @@ void drm_mm_remove_node(struct drm_mm_node *node) DRM_MM_BUG_ON(!node->allocated); DRM_MM_BUG_ON(node->scanned_block); - prev_node = - list_entry(node->node_list.prev, struct drm_mm_node, node_list); + prev_node = list_prev_entry(node, node_list); - if (drm_mm_hole_follows(node)) { - DRM_MM_BUG_ON(__drm_mm_hole_node_start(node) == - __drm_mm_hole_node_end(node)); - list_del(&node->hole_stack); - } else { - DRM_MM_BUG_ON(__drm_mm_hole_node_start(node) != - __drm_mm_hole_node_end(node)); - } - - if (!drm_mm_hole_follows(prev_node)) { - prev_node->hole_follows = 1; - list_add(&prev_node->hole_stack, &mm->hole_stack); - } else - list_move(&prev_node->hole_stack, &mm->hole_stack); + if (drm_mm_hole_follows(node)) + rm_hole(node); drm_mm_interval_tree_remove(node, &mm->interval_tree); list_del(&node->node_list); - node->allocated = 0; + node->allocated = false; + + if (drm_mm_hole_follows(prev_node)) + rm_hole(prev_node); + add_hole(prev_node); } EXPORT_SYMBOL(drm_mm_remove_node); -static int check_free_hole(u64 start, u64 end, u64 size, u64 alignment) -{ - if (end - start < size) - return 0; - - if (alignment) { - u64 rem; - - div64_u64_rem(start, alignment, &rem); - if (rem) - start += alignment - rem; - } - - return end >= start + size; -} - -static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, - u64 size, - u64 alignment, - unsigned long color, - u64 start, - u64 end, - enum drm_mm_search_flags flags) -{ - struct drm_mm_node *entry; - struct drm_mm_node *best; - u64 adj_start; - u64 adj_end; - u64 best_size; - - DRM_MM_BUG_ON(mm->scan_active); - - best = NULL; - best_size = ~0UL; - - __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, - flags & DRM_MM_SEARCH_BELOW) { - u64 hole_size = adj_end - adj_start; - - if (mm->color_adjust) { - mm->color_adjust(entry, color, &adj_start, &adj_end); - if (adj_end <= adj_start) - continue; - } - - adj_start = max(adj_start, start); - adj_end = min(adj_end, end); - - if (!check_free_hole(adj_start, adj_end, size, alignment)) - continue; - - if (!(flags & DRM_MM_SEARCH_BEST)) - return entry; - - if (hole_size < best_size) { - best = entry; - best_size = hole_size; - } - } - - return best; -} - /** * drm_mm_replace_node - move an allocation from @old to @new * @old: drm_mm_node to remove from the allocator @@ -521,18 +574,23 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) { DRM_MM_BUG_ON(!old->allocated); - list_replace(&old->node_list, &new->node_list); - list_replace(&old->hole_stack, &new->hole_stack); - rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree); - new->hole_follows = old->hole_follows; - new->mm = old->mm; - new->start = old->start; - new->size = old->size; - new->color = old->color; - new->__subtree_last = old->__subtree_last; + *new = *old; - old->allocated = 0; - new->allocated = 1; + list_replace(&old->node_list, &new->node_list); + rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree); + + if (drm_mm_hole_follows(old)) { + list_replace(&old->hole_stack, &new->hole_stack); + rb_replace_node(&old->rb_hole_size, + &new->rb_hole_size, + &old->mm->holes_size); + rb_replace_node(&old->rb_hole_addr, + &new->rb_hole_addr, + &old->mm->holes_addr); + } + + old->allocated = false; + new->allocated = true; } EXPORT_SYMBOL(drm_mm_replace_node); @@ -577,7 +635,7 @@ EXPORT_SYMBOL(drm_mm_replace_node); * @color: opaque tag value to use for the allocation * @start: start of the allowed range for the allocation * @end: end of the allowed range for the allocation - * @flags: flags to specify how the allocation will be performed afterwards + * @mode: fine-tune the allocation search and placement * * This simply sets up the scanning routines with the parameters for the desired * hole. @@ -593,7 +651,7 @@ void drm_mm_scan_init_with_range(struct drm_mm_scan *scan, unsigned long color, u64 start, u64 end, - unsigned int flags) + enum drm_mm_insert_mode mode) { DRM_MM_BUG_ON(start >= end); DRM_MM_BUG_ON(!size || size > end - start); @@ -608,7 +666,7 @@ void drm_mm_scan_init_with_range(struct drm_mm_scan *scan, scan->alignment = alignment; scan->remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0; scan->size = size; - scan->flags = flags; + scan->mode = mode; DRM_MM_BUG_ON(end <= start); scan->range_start = start; @@ -667,7 +725,7 @@ bool drm_mm_scan_add_block(struct drm_mm_scan *scan, if (adj_end <= adj_start || adj_end - adj_start < scan->size) return false; - if (scan->flags == DRM_MM_CREATE_TOP) + if (scan->mode == DRM_MM_INSERT_HIGH) adj_start = adj_end - scan->size; if (scan->alignment) { @@ -679,7 +737,7 @@ bool drm_mm_scan_add_block(struct drm_mm_scan *scan, div64_u64_rem(adj_start, scan->alignment, &rem); if (rem) { adj_start -= rem; - if (scan->flags != DRM_MM_CREATE_TOP) + if (scan->mode != DRM_MM_INSERT_HIGH) adj_start += scan->alignment; if (adj_start < max(col_start, scan->range_start) || min(col_end, scan->range_end) - adj_start < scan->size) @@ -775,7 +833,7 @@ struct drm_mm_node *drm_mm_scan_color_evict(struct drm_mm_scan *scan) hole = list_first_entry(&mm->hole_stack, typeof(*hole), hole_stack); hole_start = __drm_mm_hole_node_start(hole); - hole_end = __drm_mm_hole_node_end(hole); + hole_end = hole_start + hole->hole_size; DRM_MM_BUG_ON(hole_start > scan->hit_start); DRM_MM_BUG_ON(hole_end < scan->hit_end); @@ -802,21 +860,22 @@ void drm_mm_init(struct drm_mm *mm, u64 start, u64 size) { DRM_MM_BUG_ON(start + size <= start); + mm->color_adjust = NULL; + INIT_LIST_HEAD(&mm->hole_stack); - mm->scan_active = 0; + mm->interval_tree = RB_ROOT; + mm->holes_size = RB_ROOT; + mm->holes_addr = RB_ROOT; /* Clever trick to avoid a special case in the free hole tracking. */ INIT_LIST_HEAD(&mm->head_node.node_list); - mm->head_node.allocated = 0; - mm->head_node.hole_follows = 1; + mm->head_node.allocated = false; mm->head_node.mm = mm; mm->head_node.start = start + size; - mm->head_node.size = start - mm->head_node.start; - list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack); + mm->head_node.size = -size; + add_hole(&mm->head_node); - mm->interval_tree = RB_ROOT; - - mm->color_adjust = NULL; + mm->scan_active = 0; } EXPORT_SYMBOL(drm_mm_init); @@ -837,20 +896,17 @@ EXPORT_SYMBOL(drm_mm_takedown); static u64 drm_mm_dump_hole(struct drm_printer *p, const struct drm_mm_node *entry) { - u64 hole_start, hole_end, hole_size; + u64 start, size; - if (entry->hole_follows) { - hole_start = drm_mm_hole_node_start(entry); - hole_end = drm_mm_hole_node_end(entry); - hole_size = hole_end - hole_start; - drm_printf(p, "%#018llx-%#018llx: %llu: free\n", hole_start, - hole_end, hole_size); - return hole_size; + size = entry->hole_size; + if (size) { + start = drm_mm_hole_node_start(entry); + drm_printf(p, "%#018llx-%#018llx: %llu: free\n", + start, start + size, size); } - return 0; + return size; } - /** * drm_mm_print - print allocator state * @mm: drm_mm allocator to print diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c index 20cc33d1bfc1..d9100b565198 100644 --- a/drivers/gpu/drm/drm_vma_manager.c +++ b/drivers/gpu/drm/drm_vma_manager.c @@ -212,8 +212,7 @@ int drm_vma_offset_add(struct drm_vma_offset_manager *mgr, goto out_unlock; } - ret = drm_mm_insert_node(&mgr->vm_addr_space_mm, &node->vm_node, - pages, 0, DRM_MM_SEARCH_DEFAULT); + ret = drm_mm_insert_node(&mgr->vm_addr_space_mm, &node->vm_node, pages); if (ret) goto out_unlock; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c index ff826c16fb89..f103e787de94 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c @@ -108,6 +108,7 @@ static int etnaviv_iommu_find_iova(struct etnaviv_iommu *mmu, struct drm_mm_node *node, size_t size) { struct etnaviv_vram_mapping *free = NULL; + enum drm_mm_insert_mode mode = DRM_MM_INSERT_LOW; int ret; lockdep_assert_held(&mmu->lock); @@ -119,9 +120,9 @@ static int etnaviv_iommu_find_iova(struct etnaviv_iommu *mmu, bool found; ret = drm_mm_insert_node_in_range(&mmu->mm, node, - size, 0, mmu->last_iova, ~0UL, - DRM_MM_SEARCH_DEFAULT); - + size, 0, 0, + mmu->last_iova, U64_MAX, + mode); if (ret != -ENOSPC) break; @@ -136,7 +137,7 @@ static int etnaviv_iommu_find_iova(struct etnaviv_iommu *mmu, } /* Try to retire some entries */ - drm_mm_scan_init(&scan, &mmu->mm, size, 0, 0, 0); + drm_mm_scan_init(&scan, &mmu->mm, size, 0, 0, mode); found = 0; INIT_LIST_HEAD(&list); @@ -188,6 +189,8 @@ static int etnaviv_iommu_find_iova(struct etnaviv_iommu *mmu, list_del_init(&m->scan_node); } + mode = DRM_MM_INSERT_EVICT; + /* * We removed enough mappings so that the new allocation will * succeed, retry the allocation one more time. diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a07b62732923..c8689892a89f 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -69,12 +69,10 @@ insert_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node, u32 size) { memset(node, 0, sizeof(*node)); - return drm_mm_insert_node_in_range_generic(&ggtt->base.mm, node, - size, 0, - I915_COLOR_UNEVICTABLE, - 0, ggtt->mappable_end, - DRM_MM_SEARCH_DEFAULT, - DRM_MM_CREATE_DEFAULT); + return drm_mm_insert_node_in_range(&ggtt->base.mm, node, + size, 0, I915_COLOR_UNEVICTABLE, + 0, ggtt->mappable_end, + DRM_MM_INSERT_LOW); } static void diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index a43e44e18042..c181b1bb3d2c 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -109,6 +109,7 @@ i915_gem_evict_something(struct i915_address_space *vm, }, **phase; struct i915_vma *vma, *next; struct drm_mm_node *node; + enum drm_mm_insert_mode mode; int ret; lockdep_assert_held(&vm->i915->drm.struct_mutex); @@ -127,10 +128,14 @@ i915_gem_evict_something(struct i915_address_space *vm, * On each list, the oldest objects lie at the HEAD with the freshest * object on the TAIL. */ + mode = DRM_MM_INSERT_BEST; + if (flags & PIN_HIGH) + mode = DRM_MM_INSERT_HIGH; + if (flags & PIN_MAPPABLE) + mode = DRM_MM_INSERT_LOW; drm_mm_scan_init_with_range(&scan, &vm->mm, min_size, alignment, cache_level, - start, end, - flags & PIN_HIGH ? DRM_MM_CREATE_TOP : 0); + start, end, mode); /* Retire before we search the active list. Although we have * reasonable accuracy in our retirement lists, we may have diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index c66e90571031..57bec08e80c5 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -436,12 +436,11 @@ static void *reloc_iomap(struct drm_i915_gem_object *obj, PIN_MAPPABLE | PIN_NONBLOCK); if (IS_ERR(vma)) { memset(&cache->node, 0, sizeof(cache->node)); - ret = drm_mm_insert_node_in_range_generic + ret = drm_mm_insert_node_in_range (&ggtt->base.mm, &cache->node, PAGE_SIZE, 0, I915_COLOR_UNEVICTABLE, 0, ggtt->mappable_end, - DRM_MM_SEARCH_DEFAULT, - DRM_MM_CREATE_DEFAULT); + DRM_MM_INSERT_LOW); if (ret) /* no inactive aperture space, use cpu reloc */ return NULL; } else { diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index e808aad203d8..30d8dbd04f0b 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -2748,12 +2748,10 @@ int i915_gem_init_ggtt(struct drm_i915_private *dev_priv) return ret; /* Reserve a mappable slot for our lockless error capture */ - ret = drm_mm_insert_node_in_range_generic(&ggtt->base.mm, - &ggtt->error_capture, - PAGE_SIZE, 0, - I915_COLOR_UNEVICTABLE, - 0, ggtt->mappable_end, - 0, 0); + ret = drm_mm_insert_node_in_range(&ggtt->base.mm, &ggtt->error_capture, + PAGE_SIZE, 0, I915_COLOR_UNEVICTABLE, + 0, ggtt->mappable_end, + DRM_MM_INSERT_LOW); if (ret) return ret; @@ -3663,7 +3661,7 @@ int i915_gem_gtt_insert(struct i915_address_space *vm, u64 size, u64 alignment, unsigned long color, u64 start, u64 end, unsigned int flags) { - u32 search_flag, alloc_flag; + enum drm_mm_insert_mode mode; u64 offset; int err; @@ -3684,13 +3682,11 @@ int i915_gem_gtt_insert(struct i915_address_space *vm, if (unlikely(round_up(start, alignment) > round_down(end - size, alignment))) return -ENOSPC; - if (flags & PIN_HIGH) { - search_flag = DRM_MM_SEARCH_BELOW; - alloc_flag = DRM_MM_CREATE_TOP; - } else { - search_flag = DRM_MM_SEARCH_DEFAULT; - alloc_flag = DRM_MM_CREATE_DEFAULT; - } + mode = DRM_MM_INSERT_BEST; + if (flags & PIN_HIGH) + mode = DRM_MM_INSERT_HIGH; + if (flags & PIN_MAPPABLE) + mode = DRM_MM_INSERT_LOW; /* We only allocate in PAGE_SIZE/GTT_PAGE_SIZE (4096) chunks, * so we know that we always have a minimum alignment of 4096. @@ -3702,10 +3698,9 @@ int i915_gem_gtt_insert(struct i915_address_space *vm, if (alignment <= I915_GTT_MIN_ALIGNMENT) alignment = 0; - err = drm_mm_insert_node_in_range_generic(&vm->mm, node, - size, alignment, color, - start, end, - search_flag, alloc_flag); + err = drm_mm_insert_node_in_range(&vm->mm, node, + size, alignment, color, + start, end, mode); if (err != -ENOSPC) return err; @@ -3743,9 +3738,7 @@ int i915_gem_gtt_insert(struct i915_address_space *vm, if (err) return err; - search_flag = DRM_MM_SEARCH_DEFAULT; - return drm_mm_insert_node_in_range_generic(&vm->mm, node, - size, alignment, color, - start, end, - search_flag, alloc_flag); + return drm_mm_insert_node_in_range(&vm->mm, node, + size, alignment, color, + start, end, DRM_MM_INSERT_EVICT); } diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 127d698e7c84..ec7c5d80fe4f 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -55,9 +55,9 @@ int i915_gem_stolen_insert_node_in_range(struct drm_i915_private *dev_priv, return -ENODEV; mutex_lock(&dev_priv->mm.stolen_lock); - ret = drm_mm_insert_node_in_range(&dev_priv->mm.stolen, node, size, - alignment, start, end, - DRM_MM_SEARCH_DEFAULT); + ret = drm_mm_insert_node_in_range(&dev_priv->mm.stolen, node, + size, alignment, 0, + start, end, DRM_MM_INSERT_BEST); mutex_unlock(&dev_priv->mm.stolen_lock); return ret; diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 8098677a3916..c3b43f4d4f1f 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -54,8 +54,7 @@ static struct page **get_pages_vram(struct drm_gem_object *obj, if (!p) return ERR_PTR(-ENOMEM); - ret = drm_mm_insert_node(&priv->vram.mm, msm_obj->vram_node, - npages, 0, DRM_MM_SEARCH_DEFAULT); + ret = drm_mm_insert_node(&priv->vram.mm, msm_obj->vram_node, npages); if (ret) { drm_free_large(p); return ERR_PTR(ret); diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index a311d26ccb21..b654eca7636a 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -45,8 +45,7 @@ msm_gem_map_vma(struct msm_gem_address_space *aspace, if (WARN_ON(drm_mm_node_allocated(&vma->node))) return 0; - ret = drm_mm_insert_node(&aspace->mm, &vma->node, npages, - 0, DRM_MM_SEARCH_DEFAULT); + ret = drm_mm_insert_node(&aspace->mm, &vma->node, npages); if (ret) return ret; diff --git a/drivers/gpu/drm/selftests/test-drm_mm.c b/drivers/gpu/drm/selftests/test-drm_mm.c index 6df53e6c1308..bb5b7480e0b4 100644 --- a/drivers/gpu/drm/selftests/test-drm_mm.c +++ b/drivers/gpu/drm/selftests/test-drm_mm.c @@ -22,23 +22,24 @@ static unsigned int max_iterations = 8192; static unsigned int max_prime = 128; enum { - DEFAULT, - TOPDOWN, BEST, + BOTTOMUP, + TOPDOWN, + EVICT, }; static const struct insert_mode { const char *name; - unsigned int search_flags; - unsigned int create_flags; + enum drm_mm_insert_mode mode; } insert_modes[] = { - [DEFAULT] = { "default", DRM_MM_SEARCH_DEFAULT, DRM_MM_CREATE_DEFAULT }, - [TOPDOWN] = { "top-down", DRM_MM_SEARCH_BELOW, DRM_MM_CREATE_TOP }, - [BEST] = { "best", DRM_MM_SEARCH_BEST, DRM_MM_CREATE_DEFAULT }, + [BEST] = { "best", DRM_MM_INSERT_BEST }, + [BOTTOMUP] = { "bottom-up", DRM_MM_INSERT_LOW }, + [TOPDOWN] = { "top-down", DRM_MM_INSERT_HIGH }, + [EVICT] = { "evict", DRM_MM_INSERT_EVICT }, {} }, evict_modes[] = { - { "default", DRM_MM_SEARCH_DEFAULT, DRM_MM_CREATE_DEFAULT }, - { "top-down", DRM_MM_SEARCH_BELOW, DRM_MM_CREATE_TOP }, + { "bottom-up", DRM_MM_INSERT_LOW }, + { "top-down", DRM_MM_INSERT_HIGH }, {} }; @@ -526,8 +527,7 @@ static bool expect_insert(struct drm_mm *mm, struct drm_mm_node *node, err = drm_mm_insert_node_generic(mm, node, size, alignment, color, - mode->search_flags, - mode->create_flags); + mode->mode); if (err) { pr_err("insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d\n", size, alignment, color, mode->name, err); @@ -547,7 +547,7 @@ static bool expect_insert_fail(struct drm_mm *mm, u64 size) struct drm_mm_node tmp = {}; int err; - err = drm_mm_insert_node(mm, &tmp, size, 0, DRM_MM_SEARCH_DEFAULT); + err = drm_mm_insert_node(mm, &tmp, size); if (likely(err == -ENOSPC)) return true; @@ -753,11 +753,10 @@ static bool expect_insert_in_range(struct drm_mm *mm, struct drm_mm_node *node, { int err; - err = drm_mm_insert_node_in_range_generic(mm, node, - size, alignment, color, - range_start, range_end, - mode->search_flags, - mode->create_flags); + err = drm_mm_insert_node_in_range(mm, node, + size, alignment, color, + range_start, range_end, + mode->mode); if (err) { pr_err("insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d\n", size, alignment, color, mode->name, @@ -781,11 +780,10 @@ static bool expect_insert_in_range_fail(struct drm_mm *mm, struct drm_mm_node tmp = {}; int err; - err = drm_mm_insert_node_in_range_generic(mm, &tmp, - size, 0, 0, - range_start, range_end, - DRM_MM_SEARCH_DEFAULT, - DRM_MM_CREATE_DEFAULT); + err = drm_mm_insert_node_in_range(mm, &tmp, + size, 0, 0, + range_start, range_end, + 0); if (likely(err == -ENOSPC)) return true; @@ -1324,7 +1322,7 @@ static int evict_something(struct drm_mm *mm, drm_mm_scan_init_with_range(&scan, mm, size, alignment, 0, range_start, range_end, - mode->create_flags); + mode->mode); if (!evict_nodes(&scan, nodes, order, count, false, &evict_list)) @@ -1332,8 +1330,7 @@ static int evict_something(struct drm_mm *mm, memset(&tmp, 0, sizeof(tmp)); err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, 0, - mode->search_flags, - mode->create_flags); + DRM_MM_INSERT_EVICT); if (err) { pr_err("Failed to insert into eviction hole: size=%d, align=%d\n", size, alignment); @@ -1408,8 +1405,7 @@ static int igt_evict(void *ignored) ret = -EINVAL; drm_mm_init(&mm, 0, size); for (n = 0; n < size; n++) { - err = drm_mm_insert_node(&mm, &nodes[n].node, 1, 0, - DRM_MM_SEARCH_DEFAULT); + err = drm_mm_insert_node(&mm, &nodes[n].node, 1); if (err) { pr_err("insert failed, step %d\n", n); ret = err; @@ -1517,8 +1513,7 @@ static int igt_evict_range(void *ignored) ret = -EINVAL; drm_mm_init(&mm, 0, size); for (n = 0; n < size; n++) { - err = drm_mm_insert_node(&mm, &nodes[n].node, 1, 0, - DRM_MM_SEARCH_DEFAULT); + err = drm_mm_insert_node(&mm, &nodes[n].node, 1); if (err) { pr_err("insert failed, step %d\n", n); ret = err; @@ -1904,7 +1899,7 @@ static int evict_color(struct drm_mm *mm, drm_mm_scan_init_with_range(&scan, mm, size, alignment, color, range_start, range_end, - mode->create_flags); + mode->mode); if (!evict_nodes(&scan, nodes, order, count, true, &evict_list)) @@ -1912,8 +1907,7 @@ static int evict_color(struct drm_mm *mm, memset(&tmp, 0, sizeof(tmp)); err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, color, - mode->search_flags, - mode->create_flags); + DRM_MM_INSERT_EVICT); if (err) { pr_err("Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d\n", size, alignment, color, err); diff --git a/drivers/gpu/drm/sis/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c index 03defda77766..1622db24cd39 100644 --- a/drivers/gpu/drm/sis/sis_mm.c +++ b/drivers/gpu/drm/sis/sis_mm.c @@ -109,8 +109,7 @@ static int sis_drm_alloc(struct drm_device *dev, struct drm_file *file, if (pool == AGP_TYPE) { retval = drm_mm_insert_node(&dev_priv->agp_mm, &item->mm_node, - mem->size, 0, - DRM_MM_SEARCH_DEFAULT); + mem->size); offset = item->mm_node.start; } else { #if defined(CONFIG_FB_SIS) || defined(CONFIG_FB_SIS_MODULE) @@ -122,8 +121,7 @@ static int sis_drm_alloc(struct drm_device *dev, struct drm_file *file, #else retval = drm_mm_insert_node(&dev_priv->vram_mm, &item->mm_node, - mem->size, 0, - DRM_MM_SEARCH_DEFAULT); + mem->size); offset = item->mm_node.start; #endif } diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 7d853e6b5ff0..b523a5d4a38c 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -128,8 +128,8 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) if (!bo->mm) return -ENOMEM; - err = drm_mm_insert_node_generic(&tegra->mm, bo->mm, bo->gem.size, - PAGE_SIZE, 0, 0, 0); + err = drm_mm_insert_node_generic(&tegra->mm, + bo->mm, bo->gem.size, PAGE_SIZE, 0, 0); if (err < 0) { dev_err(tegra->drm->dev, "out of I/O virtual memory: %zd\n", err); diff --git a/drivers/gpu/drm/ttm/ttm_bo_manager.c b/drivers/gpu/drm/ttm/ttm_bo_manager.c index 988c48d1cf3e..90a6c0b03afc 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_manager.c +++ b/drivers/gpu/drm/ttm/ttm_bo_manager.c @@ -54,9 +54,8 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, { struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv; struct drm_mm *mm = &rman->mm; - struct drm_mm_node *node = NULL; - enum drm_mm_search_flags sflags = DRM_MM_SEARCH_BEST; - enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT; + struct drm_mm_node *node; + enum drm_mm_insert_mode mode; unsigned long lpfn; int ret; @@ -68,16 +67,15 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, if (!node) return -ENOMEM; - if (place->flags & TTM_PL_FLAG_TOPDOWN) { - sflags = DRM_MM_SEARCH_BELOW; - aflags = DRM_MM_CREATE_TOP; - } + mode = DRM_MM_INSERT_BEST; + if (place->flags & TTM_PL_FLAG_TOPDOWN) + mode = DRM_MM_INSERT_HIGH; spin_lock(&rman->lock); - ret = drm_mm_insert_node_in_range_generic(mm, node, mem->num_pages, + ret = drm_mm_insert_node_in_range(mm, node, + mem->num_pages, mem->page_alignment, 0, - place->fpfn, lpfn, - sflags, aflags); + place->fpfn, lpfn, mode); spin_unlock(&rman->lock); if (unlikely(ret)) { diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 63239b5b5092..a0cd4ea15f07 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -593,7 +593,7 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, spin_lock_irqsave(&vc4->hvs->mm_lock, flags); ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm, - dlist_count, 1, 0); + dlist_count); spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags); if (ret) return ret; diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index fc68b1b4da52..f7f7677f6d8d 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -141,8 +141,7 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, int ret, i; u32 __iomem *dst_kernel; - ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS, 1, - 0); + ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS); if (ret) { DRM_ERROR("Failed to allocate space for filter kernel: %d\n", ret); diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 110d1518f5d5..c1f06897136b 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -514,9 +514,9 @@ static int vc4_plane_mode_set(struct drm_plane *plane, if (lbm_size) { if (!vc4_state->lbm.allocated) { spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); - ret = drm_mm_insert_node(&vc4->hvs->lbm_mm, - &vc4_state->lbm, - lbm_size, 32, 0); + ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm, + &vc4_state->lbm, + lbm_size, 32, 0, 0); spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); } else { WARN_ON_ONCE(lbm_size != vc4_state->lbm.size); diff --git a/drivers/gpu/drm/via/via_mm.c b/drivers/gpu/drm/via/via_mm.c index a04ef1c992d9..4217d66a5cc6 100644 --- a/drivers/gpu/drm/via/via_mm.c +++ b/drivers/gpu/drm/via/via_mm.c @@ -140,11 +140,11 @@ int via_mem_alloc(struct drm_device *dev, void *data, if (mem->type == VIA_MEM_AGP) retval = drm_mm_insert_node(&dev_priv->agp_mm, &item->mm_node, - tmpSize, 0, DRM_MM_SEARCH_DEFAULT); + tmpSize); else retval = drm_mm_insert_node(&dev_priv->vram_mm, &item->mm_node, - tmpSize, 0, DRM_MM_SEARCH_DEFAULT); + tmpSize); if (retval) goto fail_alloc; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c index aa04fb0159a7..77cb7c627e09 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c @@ -673,16 +673,10 @@ static bool vmw_cmdbuf_try_alloc(struct vmw_cmdbuf_man *man, memset(info->node, 0, sizeof(*info->node)); spin_lock_bh(&man->lock); - ret = drm_mm_insert_node_generic(&man->mm, info->node, info->page_size, - 0, 0, - DRM_MM_SEARCH_DEFAULT, - DRM_MM_CREATE_DEFAULT); + ret = drm_mm_insert_node(&man->mm, info->node, info->page_size); if (ret) { vmw_cmdbuf_man_process(man); - ret = drm_mm_insert_node_generic(&man->mm, info->node, - info->page_size, 0, 0, - DRM_MM_SEARCH_DEFAULT, - DRM_MM_CREATE_DEFAULT); + ret = drm_mm_insert_node(&man->mm, info->node, info->page_size); } spin_unlock_bh(&man->lock); diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 3bddca8fd2b5..d81b0ba9921f 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -53,19 +53,62 @@ #define DRM_MM_BUG_ON(expr) BUILD_BUG_ON_INVALID(expr) #endif -enum drm_mm_search_flags { - DRM_MM_SEARCH_DEFAULT = 0, - DRM_MM_SEARCH_BEST = 1 << 0, - DRM_MM_SEARCH_BELOW = 1 << 1, -}; +/** + * enum drm_mm_insert_mode - control search and allocation behaviour + * + * The &struct drm_mm range manager supports finding a suitable modes using + * a number of search trees. These trees are oranised by size, by address and + * in most recent eviction order. This allows the user to find either the + * smallest hole to reuse, the lowest or highest address to reuse, or simply + * reuse the most recent eviction that fits. When allocating the &drm_mm_node + * from within the hole, the &drm_mm_insert_mode also dictate whether to + * allocate the lowest matching address or the highest. + */ +enum drm_mm_insert_mode { + /** + * @DRM_MM_INSERT_BEST: + * + * Search for the smallest hole (within the search range) that fits + * the desired node. + * + * Allocates the node from the bottom of the found hole. + */ + DRM_MM_INSERT_BEST = 0, -enum drm_mm_allocator_flags { - DRM_MM_CREATE_DEFAULT = 0, - DRM_MM_CREATE_TOP = 1 << 0, -}; + /** + * @DRM_MM_INSERT_LOW: + * + * Search for the lowest hole (address closest to 0, within the search + * range) that fits the desired node. + * + * Allocates the node from the bottom of the found hole. + */ + DRM_MM_INSERT_LOW, -#define DRM_MM_BOTTOMUP DRM_MM_SEARCH_DEFAULT, DRM_MM_CREATE_DEFAULT -#define DRM_MM_TOPDOWN DRM_MM_SEARCH_BELOW, DRM_MM_CREATE_TOP + /** + * @DRM_MM_INSERT_HIGH: + * + * Search for the highest hole (address closest to U64_MAX, within the + * search range) that fits the desired node. + * + * Allocates the node from the *top* of the found hole. The specified + * alignment for the node is applied to the base of the node + * (&drm_mm_node.start). + */ + DRM_MM_INSERT_HIGH, + + /** + * @DRM_MM_INSERT_EVICT: + * + * Search for the most recently evicted hole (within the search range) + * that fits the desired node. This is appropriate for use immediately + * after performing an eviction scan (see drm_mm_scan_init()) and + * removing the selected nodes to form a hole. + * + * Allocates the node from the bottom of the found hole. + */ + DRM_MM_INSERT_EVICT, +}; /** * struct drm_mm_node - allocated block in the DRM allocator @@ -84,14 +127,16 @@ struct drm_mm_node { /** @size: Size of the allocated block. */ u64 size; /* private: */ + struct drm_mm *mm; struct list_head node_list; struct list_head hole_stack; struct rb_node rb; - unsigned hole_follows : 1; - unsigned allocated : 1; - bool scanned_block : 1; + struct rb_node rb_hole_size; + struct rb_node rb_hole_addr; u64 __subtree_last; - struct drm_mm *mm; + u64 hole_size; + bool allocated : 1; + bool scanned_block : 1; #ifdef CONFIG_DRM_DEBUG_MM depot_stack_handle_t stack; #endif @@ -127,6 +172,8 @@ struct drm_mm { struct drm_mm_node head_node; /* Keep an interval_tree for fast lookup of drm_mm_nodes by address. */ struct rb_root interval_tree; + struct rb_root holes_size; + struct rb_root holes_addr; unsigned long scan_active; }; @@ -155,7 +202,7 @@ struct drm_mm_scan { u64 hit_end; unsigned long color; - unsigned int flags; + enum drm_mm_insert_mode mode; }; /** @@ -208,7 +255,7 @@ static inline bool drm_mm_initialized(const struct drm_mm *mm) */ static inline bool drm_mm_hole_follows(const struct drm_mm_node *node) { - return node->hole_follows; + return node->hole_size; } static inline u64 __drm_mm_hole_node_start(const struct drm_mm_node *hole_node) @@ -291,17 +338,9 @@ static inline u64 drm_mm_hole_node_end(const struct drm_mm_node *hole_node) #define drm_mm_for_each_node_safe(entry, next, mm) \ list_for_each_entry_safe(entry, next, drm_mm_nodes(mm), node_list) -#define __drm_mm_for_each_hole(entry, mm, hole_start, hole_end, backwards) \ - for (entry = list_entry((backwards) ? (mm)->hole_stack.prev : (mm)->hole_stack.next, struct drm_mm_node, hole_stack); \ - &entry->hole_stack != &(mm)->hole_stack ? \ - hole_start = drm_mm_hole_node_start(entry), \ - hole_end = drm_mm_hole_node_end(entry), \ - 1 : 0; \ - entry = list_entry((backwards) ? entry->hole_stack.prev : entry->hole_stack.next, struct drm_mm_node, hole_stack)) - /** * drm_mm_for_each_hole - iterator to walk over all holes - * @entry: &drm_mm_node used internally to track progress + * @pos: &drm_mm_node used internally to track progress * @mm: &drm_mm allocator to walk * @hole_start: ulong variable to assign the hole start to on each iteration * @hole_end: ulong variable to assign the hole end to on each iteration @@ -314,57 +353,28 @@ static inline u64 drm_mm_hole_node_end(const struct drm_mm_node *hole_node) * Implementation Note: * We need to inline list_for_each_entry in order to be able to set hole_start * and hole_end on each iteration while keeping the macro sane. - * - * The __drm_mm_for_each_hole version is similar, but with added support for - * going backwards. */ -#define drm_mm_for_each_hole(entry, mm, hole_start, hole_end) \ - __drm_mm_for_each_hole(entry, mm, hole_start, hole_end, 0) +#define drm_mm_for_each_hole(pos, mm, hole_start, hole_end) \ + for (pos = list_first_entry(&(mm)->hole_stack, \ + typeof(*pos), hole_stack); \ + &pos->hole_stack != &(mm)->hole_stack ? \ + hole_start = drm_mm_hole_node_start(pos), \ + hole_end = hole_start + pos->hole_size, \ + 1 : 0; \ + pos = list_next_entry(pos, hole_stack)) /* * Basic range manager support (drm_mm.c) */ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node); -int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, - struct drm_mm_node *node, - u64 size, - u64 alignment, - unsigned long color, - u64 start, - u64 end, - enum drm_mm_search_flags sflags, - enum drm_mm_allocator_flags aflags); - -/** - * drm_mm_insert_node_in_range - ranged search for space and insert @node - * @mm: drm_mm to allocate from - * @node: preallocate node to insert - * @size: size of the allocation - * @alignment: alignment of the allocation - * @start: start of the allowed range for this node - * @end: end of the allowed range for this node - * @flags: flags to fine-tune the allocation - * - * This is a simplified version of drm_mm_insert_node_in_range_generic() with - * @color set to 0. - * - * The preallocated node must be cleared to 0. - * - * Returns: - * 0 on success, -ENOSPC if there's no suitable hole. - */ -static inline int drm_mm_insert_node_in_range(struct drm_mm *mm, - struct drm_mm_node *node, - u64 size, - u64 alignment, - u64 start, - u64 end, - enum drm_mm_search_flags flags) -{ - return drm_mm_insert_node_in_range_generic(mm, node, size, alignment, - 0, start, end, flags, - DRM_MM_CREATE_DEFAULT); -} +int drm_mm_insert_node_in_range(struct drm_mm *mm, + struct drm_mm_node *node, + u64 size, + u64 alignment, + unsigned long color, + u64 start, + u64 end, + enum drm_mm_insert_mode mode); /** * drm_mm_insert_node_generic - search for space and insert @node @@ -373,8 +383,7 @@ static inline int drm_mm_insert_node_in_range(struct drm_mm *mm, * @size: size of the allocation * @alignment: alignment of the allocation * @color: opaque tag value to use for this node - * @sflags: flags to fine-tune the allocation search - * @aflags: flags to fine-tune the allocation behavior + * @mode: fine-tune the allocation search and placement * * This is a simplified version of drm_mm_insert_node_in_range_generic() with no * range restrictions applied. @@ -388,13 +397,11 @@ static inline int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, u64 size, u64 alignment, unsigned long color, - enum drm_mm_search_flags sflags, - enum drm_mm_allocator_flags aflags) + enum drm_mm_insert_mode mode) { - return drm_mm_insert_node_in_range_generic(mm, node, - size, alignment, 0, - 0, U64_MAX, - sflags, aflags); + return drm_mm_insert_node_in_range(mm, node, + size, alignment, color, + 0, U64_MAX, mode); } /** @@ -402,8 +409,6 @@ drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, * @mm: drm_mm to allocate from * @node: preallocate node to insert * @size: size of the allocation - * @alignment: alignment of the allocation - * @flags: flags to fine-tune the allocation * * This is a simplified version of drm_mm_insert_node_generic() with @color set * to 0. @@ -415,13 +420,9 @@ drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, */ static inline int drm_mm_insert_node(struct drm_mm *mm, struct drm_mm_node *node, - u64 size, - u64 alignment, - enum drm_mm_search_flags flags) + u64 size) { - return drm_mm_insert_node_generic(mm, node, - size, alignment, 0, - flags, DRM_MM_CREATE_DEFAULT); + return drm_mm_insert_node_generic(mm, node, size, 0, 0, 0); } void drm_mm_remove_node(struct drm_mm_node *node); @@ -468,7 +469,7 @@ void drm_mm_scan_init_with_range(struct drm_mm_scan *scan, struct drm_mm *mm, u64 size, u64 alignment, unsigned long color, u64 start, u64 end, - unsigned int flags); + enum drm_mm_insert_mode mode); /** * drm_mm_scan_init - initialize lru scanning @@ -477,7 +478,7 @@ void drm_mm_scan_init_with_range(struct drm_mm_scan *scan, * @size: size of the allocation * @alignment: alignment of the allocation * @color: opaque tag value to use for the allocation - * @flags: flags to specify how the allocation will be performed afterwards + * @mode: fine-tune the allocation search and placement * * This is a simplified version of drm_mm_scan_init_with_range() with no range * restrictions applied. @@ -494,12 +495,11 @@ static inline void drm_mm_scan_init(struct drm_mm_scan *scan, u64 size, u64 alignment, unsigned long color, - unsigned int flags) + enum drm_mm_insert_mode mode) { drm_mm_scan_init_with_range(scan, mm, size, alignment, color, - 0, U64_MAX, - flags); + 0, U64_MAX, mode); } bool drm_mm_scan_add_block(struct drm_mm_scan *scan, From bb18dfcc640d0551073e756e0af2ff60bea89e6d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 2 Feb 2017 11:44:34 +0000 Subject: [PATCH 47/47] drm: kselftest for drm_mm and bottom-up allocation Check that if we request bottom-up allocation from drm_mm_insert_node() we receive the next available hole from the bottom. Signed-off-by: Chris Wilson Reviewed-by: Joonas Lahtinen Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170202114434.3060-2-chris@chris-wilson.co.uk --- drivers/gpu/drm/selftests/drm_mm_selftests.h | 1 + drivers/gpu/drm/selftests/test-drm_mm.c | 100 +++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/drivers/gpu/drm/selftests/drm_mm_selftests.h b/drivers/gpu/drm/selftests/drm_mm_selftests.h index 6a4575fdc1c0..37bbdac52896 100644 --- a/drivers/gpu/drm/selftests/drm_mm_selftests.h +++ b/drivers/gpu/drm/selftests/drm_mm_selftests.h @@ -17,6 +17,7 @@ selftest(align32, igt_align32) selftest(align64, igt_align64) selftest(evict, igt_evict) selftest(evict_range, igt_evict_range) +selftest(bottomup, igt_bottomup) selftest(topdown, igt_topdown) selftest(color, igt_color) selftest(color_evict, igt_color_evict) diff --git a/drivers/gpu/drm/selftests/test-drm_mm.c b/drivers/gpu/drm/selftests/test-drm_mm.c index bb5b7480e0b4..1e71bc182ca9 100644 --- a/drivers/gpu/drm/selftests/test-drm_mm.c +++ b/drivers/gpu/drm/selftests/test-drm_mm.c @@ -1697,6 +1697,106 @@ err: return ret; } +static int igt_bottomup(void *ignored) +{ + const struct insert_mode *bottomup = &insert_modes[BOTTOMUP]; + DRM_RND_STATE(prng, random_seed); + const unsigned int count = 8192; + unsigned int size; + unsigned long *bitmap; + struct drm_mm mm; + struct drm_mm_node *nodes, *node, *next; + unsigned int *order, n, m, o = 0; + int ret; + + /* Like igt_topdown, but instead of searching for the last hole, + * we search for the first. + */ + + ret = -ENOMEM; + nodes = vzalloc(count * sizeof(*nodes)); + if (!nodes) + goto err; + + bitmap = kzalloc(count / BITS_PER_LONG * sizeof(unsigned long), + GFP_TEMPORARY); + if (!bitmap) + goto err_nodes; + + order = drm_random_order(count, &prng); + if (!order) + goto err_bitmap; + + ret = -EINVAL; + for (size = 1; size <= 64; size <<= 1) { + drm_mm_init(&mm, 0, size*count); + for (n = 0; n < count; n++) { + if (!expect_insert(&mm, &nodes[n], + size, 0, n, + bottomup)) { + pr_err("bottomup insert failed, size %u step %d\n", size, n); + goto out; + } + + if (!assert_one_hole(&mm, size*(n + 1), size*count)) + goto out; + } + + if (!assert_continuous(&mm, size)) + goto out; + + drm_random_reorder(order, count, &prng); + for_each_prime_number_from(n, 1, min(count, max_prime)) { + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + drm_mm_remove_node(node); + __set_bit(node_index(node), bitmap); + } + + for (m = 0; m < n; m++) { + unsigned int first; + + node = &nodes[order[(o + m) % count]]; + if (!expect_insert(&mm, node, + size, 0, 0, + bottomup)) { + pr_err("insert failed, step %d/%d\n", m, n); + goto out; + } + + first = find_first_bit(bitmap, count); + if (node_index(node) != first) { + pr_err("node %d/%d not inserted into bottom hole, expected %d, found %d\n", + m, n, first, node_index(node)); + goto out; + } + __clear_bit(first, bitmap); + } + + DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count); + + o += n; + } + + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + DRM_MM_BUG_ON(!drm_mm_clean(&mm)); + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_bitmap: + kfree(bitmap); +err_nodes: + vfree(nodes); +err: + return ret; +} + static void separate_adjacent_colors(const struct drm_mm_node *node, unsigned long color, u64 *start,