diff options
| author | Dave Airlie <airlied@redhat.com> | 2017-06-19 21:07:03 -0400 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2017-06-19 21:07:03 -0400 |
| commit | 4a525bad68b424d167042342db19d0c0e80e8b29 (patch) | |
| tree | 95cb33d8752caaa9117644b0dde5e51845e78773 | |
| parent | 925344ccc91d7a7fd84cab2dece1c34bbd86fd8c (diff) | |
| parent | 43240bbd871e2c8f89584d369278a3d18680d9ea (diff) | |
Merge tag 'drm/tegra/for-4.13-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next
drm/tegra: Changes for v4.13-rc1
This starts off with the addition of more documentation for the host1x
and DRM drivers and finishes with a slew of fixes and enhancements for
the staging IOCTLs as a result of the awesome work done by Dmitry and
Erik on the grate reverse-engineering effort.
* tag 'drm/tegra/for-4.13-rc1' of git://anongit.freedesktop.org/tegra/linux:
gpu: host1x: At first try a non-blocking allocation for the gather copy
gpu: host1x: Refactor channel allocation code
gpu: host1x: Remove unused host1x_cdma_stop() definition
gpu: host1x: Remove unused 'struct host1x_cmdbuf'
gpu: host1x: Check waits in the firewall
gpu: host1x: Correct swapped arguments in the is_addr_reg() definition
gpu: host1x: Forbid unrelated SETCLASS opcode in the firewall
gpu: host1x: Forbid RESTART opcode in the firewall
gpu: host1x: Forbid relocation address shifting in the firewall
gpu: host1x: Do not leak BO's phys address to userspace
gpu: host1x: Correct host1x_job_pin() error handling
gpu: host1x: Initialize firewall class to the job's one
drm/tegra: dc: Disable plane if it is invisible
drm/tegra: dc: Apply clipping to the plane
drm/tegra: dc: Avoid reset asserts on Tegra20
drm/tegra: Check syncpoint ID in the 'submit' IOCTL
drm/tegra: Correct copying of waitchecks and disable them in the 'submit' IOCTL
drm/tegra: Check for malformed offsets and sizes in the 'submit' IOCTL
drm/tegra: Add driver documentation
gpu: host1x: Flesh out kerneldoc
| -rw-r--r-- | Documentation/gpu/index.rst | 1 | ||||
| -rw-r--r-- | Documentation/gpu/tegra.rst | 178 | ||||
| -rw-r--r-- | drivers/gpu/drm/tegra/dc.c | 92 | ||||
| -rw-r--r-- | drivers/gpu/drm/tegra/drm.c | 119 | ||||
| -rw-r--r-- | drivers/gpu/drm/tegra/drm.h | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/tegra/gem.c | 5 | ||||
| -rw-r--r-- | drivers/gpu/drm/tegra/gem.h | 5 | ||||
| -rw-r--r-- | drivers/gpu/drm/tegra/gr2d.c | 11 | ||||
| -rw-r--r-- | drivers/gpu/drm/tegra/gr3d.c | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/tegra/vic.c | 4 | ||||
| -rw-r--r-- | drivers/gpu/host1x/bus.c | 75 | ||||
| -rw-r--r-- | drivers/gpu/host1x/cdma.h | 1 | ||||
| -rw-r--r-- | drivers/gpu/host1x/channel.c | 147 | ||||
| -rw-r--r-- | drivers/gpu/host1x/channel.h | 21 | ||||
| -rw-r--r-- | drivers/gpu/host1x/debug.c | 47 | ||||
| -rw-r--r-- | drivers/gpu/host1x/dev.c | 7 | ||||
| -rw-r--r-- | drivers/gpu/host1x/dev.h | 6 | ||||
| -rw-r--r-- | drivers/gpu/host1x/hw/channel_hw.c | 4 | ||||
| -rw-r--r-- | drivers/gpu/host1x/job.c | 124 | ||||
| -rw-r--r-- | drivers/gpu/host1x/job.h | 14 | ||||
| -rw-r--r-- | drivers/gpu/host1x/syncpt.c | 81 | ||||
| -rw-r--r-- | include/linux/host1x.h | 38 |
22 files changed, 784 insertions, 201 deletions
diff --git a/Documentation/gpu/index.rst b/Documentation/gpu/index.rst index 037a39ac1807..35d673bf9b56 100644 --- a/Documentation/gpu/index.rst +++ b/Documentation/gpu/index.rst | |||
| @@ -13,6 +13,7 @@ Linux GPU Driver Developer's Guide | |||
| 13 | i915 | 13 | i915 |
| 14 | meson | 14 | meson |
| 15 | pl111 | 15 | pl111 |
| 16 | tegra | ||
| 16 | tinydrm | 17 | tinydrm |
| 17 | vc4 | 18 | vc4 |
| 18 | vga-switcheroo | 19 | vga-switcheroo |
diff --git a/Documentation/gpu/tegra.rst b/Documentation/gpu/tegra.rst new file mode 100644 index 000000000000..d2ed8938ca43 --- /dev/null +++ b/Documentation/gpu/tegra.rst | |||
| @@ -0,0 +1,178 @@ | |||
| 1 | =============================================== | ||
| 2 | drm/tegra NVIDIA Tegra GPU and display driver | ||
| 3 | =============================================== | ||
| 4 | |||
| 5 | NVIDIA Tegra SoCs support a set of display, graphics and video functions via | ||
| 6 | the host1x controller. host1x supplies command streams, gathered from a push | ||
| 7 | buffer provided directly by the CPU, to its clients via channels. Software, | ||
| 8 | or blocks amongst themselves, can use syncpoints for synchronization. | ||
| 9 | |||
| 10 | Up until, but not including, Tegra124 (aka Tegra K1) the drm/tegra driver | ||
| 11 | supports the built-in GPU, comprised of the gr2d and gr3d engines. Starting | ||
| 12 | with Tegra124 the GPU is based on the NVIDIA desktop GPU architecture and | ||
| 13 | supported by the drm/nouveau driver. | ||
| 14 | |||
| 15 | The drm/tegra driver supports NVIDIA Tegra SoC generations since Tegra20. It | ||
| 16 | has three parts: | ||
| 17 | |||
| 18 | - A host1x driver that provides infrastructure and access to the host1x | ||
| 19 | services. | ||
| 20 | |||
| 21 | - A KMS driver that supports the display controllers as well as a number of | ||
| 22 | outputs, such as RGB, HDMI, DSI, and DisplayPort. | ||
| 23 | |||
| 24 | - A set of custom userspace IOCTLs that can be used to submit jobs to the | ||
| 25 | GPU and video engines via host1x. | ||
| 26 | |||
| 27 | Driver Infrastructure | ||
| 28 | ===================== | ||
| 29 | |||
| 30 | The various host1x clients need to be bound together into a logical device in | ||
| 31 | order to expose their functionality to users. The infrastructure that supports | ||
| 32 | this is implemented in the host1x driver. When a driver is registered with the | ||
| 33 | infrastructure it provides a list of compatible strings specifying the devices | ||
| 34 | that it needs. The infrastructure creates a logical device and scan the device | ||
| 35 | tree for matching device nodes, adding the required clients to a list. Drivers | ||
| 36 | for individual clients register with the infrastructure as well and are added | ||
| 37 | to the logical host1x device. | ||
| 38 | |||
| 39 | Once all clients are available, the infrastructure will initialize the logical | ||
| 40 | device using a driver-provided function which will set up the bits specific to | ||
| 41 | the subsystem and in turn initialize each of its clients. | ||
| 42 | |||
| 43 | Similarly, when one of the clients is unregistered, the infrastructure will | ||
| 44 | destroy the logical device by calling back into the driver, which ensures that | ||
| 45 | the subsystem specific bits are torn down and the clients destroyed in turn. | ||
| 46 | |||
| 47 | Host1x Infrastructure Reference | ||
| 48 | ------------------------------- | ||
| 49 | |||
| 50 | .. kernel-doc:: include/linux/host1x.h | ||
| 51 | |||
| 52 | .. kernel-doc:: drivers/gpu/host1x/bus.c | ||
| 53 | :export: | ||
| 54 | |||
| 55 | Host1x Syncpoint Reference | ||
| 56 | -------------------------- | ||
| 57 | |||
| 58 | .. kernel-doc:: drivers/gpu/host1x/syncpt.c | ||
| 59 | :export: | ||
| 60 | |||
| 61 | KMS driver | ||
| 62 | ========== | ||
| 63 | |||
| 64 | The display hardware has remained mostly backwards compatible over the various | ||
| 65 | Tegra SoC generations, up until Tegra186 which introduces several changes that | ||
| 66 | make it difficult to support with a parameterized driver. | ||
| 67 | |||
| 68 | Display Controllers | ||
| 69 | ------------------- | ||
| 70 | |||
| 71 | Tegra SoCs have two display controllers, each of which can be associated with | ||
| 72 | zero or more outputs. Outputs can also share a single display controller, but | ||
| 73 | only if they run with compatible display timings. Two display controllers can | ||
| 74 | also share a single framebuffer, allowing cloned configurations even if modes | ||
| 75 | on two outputs don't match. A display controller is modelled as a CRTC in KMS | ||
| 76 | terms. | ||
| 77 | |||
| 78 | On Tegra186, the number of display controllers has been increased to three. A | ||
| 79 | display controller can no longer drive all of the outputs. While two of these | ||
| 80 | controllers can drive both DSI outputs and both SOR outputs, the third cannot | ||
| 81 | drive any DSI. | ||
| 82 | |||
| 83 | Windows | ||
| 84 | ~~~~~~~ | ||
| 85 | |||
| 86 | A display controller controls a set of windows that can be used to composite | ||
| 87 | multiple buffers onto the screen. While it is possible to assign arbitrary Z | ||
| 88 | ordering to individual windows (by programming the corresponding blending | ||
| 89 | registers), this is currently not supported by the driver. Instead, it will | ||
| 90 | assume a fixed Z ordering of the windows (window A is the root window, that | ||
| 91 | is, the lowest, while windows B and C are overlaid on top of window A). The | ||
| 92 | overlay windows support multiple pixel formats and can automatically convert | ||
| 93 | from YUV to RGB at scanout time. This makes them useful for displaying video | ||
| 94 | content. In KMS, each window is modelled as a plane. Each display controller | ||
| 95 | has a hardware cursor that is exposed as a cursor plane. | ||
| 96 | |||
| 97 | Outputs | ||
| 98 | ------- | ||
| 99 | |||
| 100 | The type and number of supported outputs varies between Tegra SoC generations. | ||
| 101 | All generations support at least HDMI. While earlier generations supported the | ||
| 102 | very simple RGB interfaces (one per display controller), recent generations no | ||
| 103 | longer do and instead provide standard interfaces such as DSI and eDP/DP. | ||
| 104 | |||
| 105 | Outputs are modelled as a composite encoder/connector pair. | ||
| 106 | |||
| 107 | RGB/LVDS | ||
| 108 | ~~~~~~~~ | ||
| 109 | |||
| 110 | This interface is no longer available since Tegra124. It has been replaced by | ||
| 111 | the more standard DSI and eDP interfaces. | ||
| 112 | |||
| 113 | HDMI | ||
| 114 | ~~~~ | ||
| 115 | |||
| 116 | HDMI is supported on all Tegra SoCs. Starting with Tegra210, HDMI is provided | ||
| 117 | by the versatile SOR output, which supports eDP, DP and HDMI. The SOR is able | ||
| 118 | to support HDMI 2.0, though support for this is currently not merged. | ||
| 119 | |||
| 120 | DSI | ||
| 121 | ~~~ | ||
| 122 | |||
| 123 | Although Tegra has supported DSI since Tegra30, the controller has changed in | ||
| 124 | several ways in Tegra114. Since none of the publicly available development | ||
| 125 | boards prior to Dalmore (Tegra114) have made use of DSI, only Tegra114 and | ||
| 126 | later are supported by the drm/tegra driver. | ||
| 127 | |||
| 128 | eDP/DP | ||
| 129 | ~~~~~~ | ||
| 130 | |||
| 131 | eDP was first introduced in Tegra124 where it was used to drive the display | ||
| 132 | panel for notebook form factors. Tegra210 added support for full DisplayPort | ||
| 133 | support, though this is currently not implemented in the drm/tegra driver. | ||
| 134 | |||
| 135 | Userspace Interface | ||
| 136 | =================== | ||
| 137 | |||
| 138 | The userspace interface provided by drm/tegra allows applications to create | ||
| 139 | GEM buffers, access and control syncpoints as well as submit command streams | ||
| 140 | to host1x. | ||
| 141 | |||
| 142 | GEM Buffers | ||
| 143 | ----------- | ||
| 144 | |||
| 145 | The ``DRM_IOCTL_TEGRA_GEM_CREATE`` IOCTL is used to create a GEM buffer object | ||
| 146 | with Tegra-specific flags. This is useful for buffers that should be tiled, or | ||
| 147 | that are to be scanned out upside down (useful for 3D content). | ||
| 148 | |||
| 149 | After a GEM buffer object has been created, its memory can be mapped by an | ||
| 150 | application using the mmap offset returned by the ``DRM_IOCTL_TEGRA_GEM_MMAP`` | ||
| 151 | IOCTL. | ||
| 152 | |||
| 153 | Syncpoints | ||
| 154 | ---------- | ||
| 155 | |||
| 156 | The current value of a syncpoint can be obtained by executing the | ||
| 157 | ``DRM_IOCTL_TEGRA_SYNCPT_READ`` IOCTL. Incrementing the syncpoint is achieved | ||
| 158 | using the ``DRM_IOCTL_TEGRA_SYNCPT_INCR`` IOCTL. | ||
| 159 | |||
| 160 | Userspace can also request blocking on a syncpoint. To do so, it needs to | ||
| 161 | execute the ``DRM_IOCTL_TEGRA_SYNCPT_WAIT`` IOCTL, specifying the value of | ||
| 162 | the syncpoint to wait for. The kernel will release the application when the | ||
| 163 | syncpoint reaches that value or after a specified timeout. | ||
| 164 | |||
| 165 | Command Stream Submission | ||
| 166 | ------------------------- | ||
| 167 | |||
| 168 | Before an application can submit command streams to host1x it needs to open a | ||
| 169 | channel to an engine using the ``DRM_IOCTL_TEGRA_OPEN_CHANNEL`` IOCTL. Client | ||
| 170 | IDs are used to identify the target of the channel. When a channel is no | ||
| 171 | longer needed, it can be closed using the ``DRM_IOCTL_TEGRA_CLOSE_CHANNEL`` | ||
| 172 | IOCTL. To retrieve the syncpoint associated with a channel, an application | ||
| 173 | can use the ``DRM_IOCTL_TEGRA_GET_SYNCPT``. | ||
| 174 | |||
| 175 | After opening a channel, submitting command streams is easy. The application | ||
| 176 | writes commands into the memory backing a GEM buffer object and passes these | ||
| 177 | to the ``DRM_IOCTL_TEGRA_SUBMIT`` IOCTL along with various other parameters, | ||
| 178 | such as the syncpoints or relocations used in the job submission. | ||
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 95b373f739f2..c875f11786b9 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c | |||
| @@ -30,6 +30,7 @@ struct tegra_dc_soc_info { | |||
| 30 | bool supports_block_linear; | 30 | bool supports_block_linear; |
| 31 | unsigned int pitch_align; | 31 | unsigned int pitch_align; |
| 32 | bool has_powergate; | 32 | bool has_powergate; |
| 33 | bool broken_reset; | ||
| 33 | }; | 34 | }; |
| 34 | 35 | ||
| 35 | struct tegra_plane { | 36 | struct tegra_plane { |
| @@ -485,12 +486,25 @@ static int tegra_plane_state_add(struct tegra_plane *plane, | |||
| 485 | { | 486 | { |
| 486 | struct drm_crtc_state *crtc_state; | 487 | struct drm_crtc_state *crtc_state; |
| 487 | struct tegra_dc_state *tegra; | 488 | struct tegra_dc_state *tegra; |
| 489 | struct drm_rect clip; | ||
| 490 | int err; | ||
| 488 | 491 | ||
| 489 | /* Propagate errors from allocation or locking failures. */ | 492 | /* Propagate errors from allocation or locking failures. */ |
| 490 | crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); | 493 | crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); |
| 491 | if (IS_ERR(crtc_state)) | 494 | if (IS_ERR(crtc_state)) |
| 492 | return PTR_ERR(crtc_state); | 495 | return PTR_ERR(crtc_state); |
| 493 | 496 | ||
| 497 | clip.x1 = 0; | ||
| 498 | clip.y1 = 0; | ||
| 499 | clip.x2 = crtc_state->mode.hdisplay; | ||
| 500 | clip.y2 = crtc_state->mode.vdisplay; | ||
| 501 | |||
| 502 | /* Check plane state for visibility and calculate clipping bounds */ | ||
| 503 | err = drm_plane_helper_check_state(state, &clip, 0, INT_MAX, | ||
| 504 | true, true); | ||
| 505 | if (err < 0) | ||
| 506 | return err; | ||
| 507 | |||
| 494 | tegra = to_dc_state(crtc_state); | 508 | tegra = to_dc_state(crtc_state); |
| 495 | 509 | ||
| 496 | tegra->planes |= WIN_A_ACT_REQ << plane->index; | 510 | tegra->planes |= WIN_A_ACT_REQ << plane->index; |
| @@ -545,6 +559,23 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, | |||
| 545 | return 0; | 559 | return 0; |
| 546 | } | 560 | } |
| 547 | 561 | ||
| 562 | static void tegra_dc_disable_window(struct tegra_dc *dc, int index) | ||
| 563 | { | ||
| 564 | unsigned long flags; | ||
| 565 | u32 value; | ||
| 566 | |||
| 567 | spin_lock_irqsave(&dc->lock, flags); | ||
| 568 | |||
| 569 | value = WINDOW_A_SELECT << index; | ||
| 570 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 571 | |||
| 572 | value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); | ||
| 573 | value &= ~WIN_ENABLE; | ||
| 574 | tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); | ||
| 575 | |||
| 576 | spin_unlock_irqrestore(&dc->lock, flags); | ||
| 577 | } | ||
| 578 | |||
| 548 | static void tegra_plane_atomic_update(struct drm_plane *plane, | 579 | static void tegra_plane_atomic_update(struct drm_plane *plane, |
| 549 | struct drm_plane_state *old_state) | 580 | struct drm_plane_state *old_state) |
| 550 | { | 581 | { |
| @@ -559,15 +590,18 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, | |||
| 559 | if (!plane->state->crtc || !plane->state->fb) | 590 | if (!plane->state->crtc || !plane->state->fb) |
| 560 | return; | 591 | return; |
| 561 | 592 | ||
| 593 | if (!plane->state->visible) | ||
| 594 | return tegra_dc_disable_window(dc, p->index); | ||
| 595 | |||
| 562 | memset(&window, 0, sizeof(window)); | 596 | memset(&window, 0, sizeof(window)); |
| 563 | window.src.x = plane->state->src_x >> 16; | 597 | window.src.x = plane->state->src.x1 >> 16; |
| 564 | window.src.y = plane->state->src_y >> 16; | 598 | window.src.y = plane->state->src.y1 >> 16; |
| 565 | window.src.w = plane->state->src_w >> 16; | 599 | window.src.w = drm_rect_width(&plane->state->src) >> 16; |
| 566 | window.src.h = plane->state->src_h >> 16; | 600 | window.src.h = drm_rect_height(&plane->state->src) >> 16; |
| 567 | window.dst.x = plane->state->crtc_x; | 601 | window.dst.x = plane->state->dst.x1; |
| 568 | window.dst.y = plane->state->crtc_y; | 602 | window.dst.y = plane->state->dst.y1; |
| 569 | window.dst.w = plane->state->crtc_w; | 603 | window.dst.w = drm_rect_width(&plane->state->dst); |
| 570 | window.dst.h = plane->state->crtc_h; | 604 | window.dst.h = drm_rect_height(&plane->state->dst); |
| 571 | window.bits_per_pixel = fb->format->cpp[0] * 8; | 605 | window.bits_per_pixel = fb->format->cpp[0] * 8; |
| 572 | window.bottom_up = tegra_fb_is_bottom_up(fb); | 606 | window.bottom_up = tegra_fb_is_bottom_up(fb); |
| 573 | 607 | ||
| @@ -598,8 +632,6 @@ static void tegra_plane_atomic_disable(struct drm_plane *plane, | |||
| 598 | { | 632 | { |
| 599 | struct tegra_plane *p = to_tegra_plane(plane); | 633 | struct tegra_plane *p = to_tegra_plane(plane); |
| 600 | struct tegra_dc *dc; | 634 | struct tegra_dc *dc; |
| 601 | unsigned long flags; | ||
| 602 | u32 value; | ||
| 603 | 635 | ||
| 604 | /* rien ne va plus */ | 636 | /* rien ne va plus */ |
| 605 | if (!old_state || !old_state->crtc) | 637 | if (!old_state || !old_state->crtc) |
| @@ -607,16 +639,7 @@ static void tegra_plane_atomic_disable(struct drm_plane *plane, | |||
| 607 | 639 | ||
| 608 | dc = to_tegra_dc(old_state->crtc); | 640 | dc = to_tegra_dc(old_state->crtc); |
| 609 | 641 | ||
| 610 | spin_lock_irqsave(&dc->lock, flags); | 642 | tegra_dc_disable_window(dc, p->index); |
| 611 | |||
| 612 | value = WINDOW_A_SELECT << p->index; | ||
| 613 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 614 | |||
| 615 | value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); | ||
| 616 | value &= ~WIN_ENABLE; | ||
| 617 | tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); | ||
| 618 | |||
| 619 | spin_unlock_irqrestore(&dc->lock, flags); | ||
| 620 | } | 643 | } |
| 621 | 644 | ||
| 622 | static const struct drm_plane_helper_funcs tegra_primary_plane_helper_funcs = { | 645 | static const struct drm_plane_helper_funcs tegra_primary_plane_helper_funcs = { |
| @@ -1856,6 +1879,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { | |||
| 1856 | .supports_block_linear = false, | 1879 | .supports_block_linear = false, |
| 1857 | .pitch_align = 8, | 1880 | .pitch_align = 8, |
| 1858 | .has_powergate = false, | 1881 | .has_powergate = false, |
| 1882 | .broken_reset = true, | ||
| 1859 | }; | 1883 | }; |
| 1860 | 1884 | ||
| 1861 | static const struct tegra_dc_soc_info tegra30_dc_soc_info = { | 1885 | static const struct tegra_dc_soc_info tegra30_dc_soc_info = { |
| @@ -1865,6 +1889,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { | |||
| 1865 | .supports_block_linear = false, | 1889 | .supports_block_linear = false, |
| 1866 | .pitch_align = 8, | 1890 | .pitch_align = 8, |
| 1867 | .has_powergate = false, | 1891 | .has_powergate = false, |
| 1892 | .broken_reset = false, | ||
| 1868 | }; | 1893 | }; |
| 1869 | 1894 | ||
| 1870 | static const struct tegra_dc_soc_info tegra114_dc_soc_info = { | 1895 | static const struct tegra_dc_soc_info tegra114_dc_soc_info = { |
| @@ -1874,6 +1899,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { | |||
| 1874 | .supports_block_linear = false, | 1899 | .supports_block_linear = false, |
| 1875 | .pitch_align = 64, | 1900 | .pitch_align = 64, |
| 1876 | .has_powergate = true, | 1901 | .has_powergate = true, |
| 1902 | .broken_reset = false, | ||
| 1877 | }; | 1903 | }; |
| 1878 | 1904 | ||
| 1879 | static const struct tegra_dc_soc_info tegra124_dc_soc_info = { | 1905 | static const struct tegra_dc_soc_info tegra124_dc_soc_info = { |
| @@ -1883,6 +1909,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { | |||
| 1883 | .supports_block_linear = true, | 1909 | .supports_block_linear = true, |
| 1884 | .pitch_align = 64, | 1910 | .pitch_align = 64, |
| 1885 | .has_powergate = true, | 1911 | .has_powergate = true, |
| 1912 | .broken_reset = false, | ||
| 1886 | }; | 1913 | }; |
| 1887 | 1914 | ||
| 1888 | static const struct tegra_dc_soc_info tegra210_dc_soc_info = { | 1915 | static const struct tegra_dc_soc_info tegra210_dc_soc_info = { |
| @@ -1892,6 +1919,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { | |||
| 1892 | .supports_block_linear = true, | 1919 | .supports_block_linear = true, |
| 1893 | .pitch_align = 64, | 1920 | .pitch_align = 64, |
| 1894 | .has_powergate = true, | 1921 | .has_powergate = true, |
| 1922 | .broken_reset = false, | ||
| 1895 | }; | 1923 | }; |
| 1896 | 1924 | ||
| 1897 | static const struct of_device_id tegra_dc_of_match[] = { | 1925 | static const struct of_device_id tegra_dc_of_match[] = { |
| @@ -1989,7 +2017,8 @@ static int tegra_dc_probe(struct platform_device *pdev) | |||
| 1989 | return PTR_ERR(dc->rst); | 2017 | return PTR_ERR(dc->rst); |
| 1990 | } | 2018 | } |
| 1991 | 2019 | ||
| 1992 | reset_control_assert(dc->rst); | 2020 | if (!dc->soc->broken_reset) |
| 2021 | reset_control_assert(dc->rst); | ||
| 1993 | 2022 | ||
| 1994 | if (dc->soc->has_powergate) { | 2023 | if (dc->soc->has_powergate) { |
| 1995 | if (dc->pipe == 0) | 2024 | if (dc->pipe == 0) |
| @@ -2063,10 +2092,12 @@ static int tegra_dc_suspend(struct device *dev) | |||
| 2063 | struct tegra_dc *dc = dev_get_drvdata(dev); | 2092 | struct tegra_dc *dc = dev_get_drvdata(dev); |
| 2064 | int err; | 2093 | int err; |
| 2065 | 2094 | ||
| 2066 | err = reset_control_assert(dc->rst); | 2095 | if (!dc->soc->broken_reset) { |
| 2067 | if (err < 0) { | 2096 | err = reset_control_assert(dc->rst); |
| 2068 | dev_err(dev, "failed to assert reset: %d\n", err); | 2097 | if (err < 0) { |
| 2069 | return err; | 2098 | dev_err(dev, "failed to assert reset: %d\n", err); |
| 2099 | return err; | ||
| 2100 | } | ||
| 2070 | } | 2101 | } |
| 2071 | 2102 | ||
| 2072 | if (dc->soc->has_powergate) | 2103 | if (dc->soc->has_powergate) |
| @@ -2096,10 +2127,13 @@ static int tegra_dc_resume(struct device *dev) | |||
| 2096 | return err; | 2127 | return err; |
| 2097 | } | 2128 | } |
| 2098 | 2129 | ||
| 2099 | err = reset_control_deassert(dc->rst); | 2130 | if (!dc->soc->broken_reset) { |
| 2100 | if (err < 0) { | 2131 | err = reset_control_deassert(dc->rst); |
| 2101 | dev_err(dev, "failed to deassert reset: %d\n", err); | 2132 | if (err < 0) { |
| 2102 | return err; | 2133 | dev_err(dev, |
| 2134 | "failed to deassert reset: %d\n", err); | ||
| 2135 | return err; | ||
| 2136 | } | ||
| 2103 | } | 2137 | } |
| 2104 | } | 2138 | } |
| 2105 | 2139 | ||
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 51c48a8e00ec..ac15cc65af36 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | #define DRIVER_PATCHLEVEL 0 | 26 | #define DRIVER_PATCHLEVEL 0 |
| 27 | 27 | ||
| 28 | #define CARVEOUT_SZ SZ_64M | 28 | #define CARVEOUT_SZ SZ_64M |
| 29 | #define CDMA_GATHER_FETCHES_MAX_NB 16383 | ||
| 29 | 30 | ||
| 30 | struct tegra_drm_file { | 31 | struct tegra_drm_file { |
| 31 | struct idr contexts; | 32 | struct idr contexts; |
| @@ -348,6 +349,36 @@ static int host1x_reloc_copy_from_user(struct host1x_reloc *dest, | |||
| 348 | return 0; | 349 | return 0; |
| 349 | } | 350 | } |
| 350 | 351 | ||
| 352 | static int host1x_waitchk_copy_from_user(struct host1x_waitchk *dest, | ||
| 353 | struct drm_tegra_waitchk __user *src, | ||
| 354 | struct drm_file *file) | ||
| 355 | { | ||
| 356 | u32 cmdbuf; | ||
| 357 | int err; | ||
| 358 | |||
| 359 | err = get_user(cmdbuf, &src->handle); | ||
| 360 | if (err < 0) | ||
| 361 | return err; | ||
| 362 | |||
| 363 | err = get_user(dest->offset, &src->offset); | ||
| 364 | if (err < 0) | ||
| 365 | return err; | ||
| 366 | |||
| 367 | err = get_user(dest->syncpt_id, &src->syncpt); | ||
| 368 | if (err < 0) | ||
| 369 | return err; | ||
| 370 | |||
| 371 | err = get_user(dest->thresh, &src->thresh); | ||
| 372 | if (err < 0) | ||
| 373 | return err; | ||
| 374 | |||
| 375 | dest->bo = host1x_bo_lookup(file, cmdbuf); | ||
| 376 | if (!dest->bo) | ||
| 377 | return -ENOENT; | ||
| 378 | |||
| 379 | return 0; | ||
| 380 | } | ||
| 381 | |||
| 351 | int tegra_drm_submit(struct tegra_drm_context *context, | 382 | int tegra_drm_submit(struct tegra_drm_context *context, |
| 352 | struct drm_tegra_submit *args, struct drm_device *drm, | 383 | struct drm_tegra_submit *args, struct drm_device *drm, |
| 353 | struct drm_file *file) | 384 | struct drm_file *file) |
| @@ -362,6 +393,8 @@ int tegra_drm_submit(struct tegra_drm_context *context, | |||
| 362 | struct drm_tegra_waitchk __user *waitchks = | 393 | struct drm_tegra_waitchk __user *waitchks = |
| 363 | (void __user *)(uintptr_t)args->waitchks; | 394 | (void __user *)(uintptr_t)args->waitchks; |
| 364 | struct drm_tegra_syncpt syncpt; | 395 | struct drm_tegra_syncpt syncpt; |
| 396 | struct host1x *host1x = dev_get_drvdata(drm->dev->parent); | ||
| 397 | struct host1x_syncpt *sp; | ||
| 365 | struct host1x_job *job; | 398 | struct host1x_job *job; |
| 366 | int err; | 399 | int err; |
| 367 | 400 | ||
| @@ -369,6 +402,10 @@ int tegra_drm_submit(struct tegra_drm_context *context, | |||
| 369 | if (args->num_syncpts != 1) | 402 | if (args->num_syncpts != 1) |
| 370 | return -EINVAL; | 403 | return -EINVAL; |
| 371 | 404 | ||
| 405 | /* We don't yet support waitchks */ | ||
| 406 | if (args->num_waitchks != 0) | ||
| 407 | return -EINVAL; | ||
| 408 | |||
| 372 | job = host1x_job_alloc(context->channel, args->num_cmdbufs, | 409 | job = host1x_job_alloc(context->channel, args->num_cmdbufs, |
| 373 | args->num_relocs, args->num_waitchks); | 410 | args->num_relocs, args->num_waitchks); |
| 374 | if (!job) | 411 | if (!job) |
| @@ -383,18 +420,42 @@ int tegra_drm_submit(struct tegra_drm_context *context, | |||
| 383 | while (num_cmdbufs) { | 420 | while (num_cmdbufs) { |
| 384 | struct drm_tegra_cmdbuf cmdbuf; | 421 | struct drm_tegra_cmdbuf cmdbuf; |
| 385 | struct host1x_bo *bo; | 422 | struct host1x_bo *bo; |
| 423 | struct tegra_bo *obj; | ||
| 424 | u64 offset; | ||
| 386 | 425 | ||
| 387 | if (copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf))) { | 426 | if (copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf))) { |
| 388 | err = -EFAULT; | 427 | err = -EFAULT; |
| 389 | goto fail; | 428 | goto fail; |
| 390 | } | 429 | } |
| 391 | 430 | ||
| 431 | /* | ||
| 432 | * The maximum number of CDMA gather fetches is 16383, a higher | ||
| 433 | * value means the words count is malformed. | ||
| 434 | */ | ||
| 435 | if (cmdbuf.words > CDMA_GATHER_FETCHES_MAX_NB) { | ||
| 436 | err = -EINVAL; | ||
| 437 | goto fail; | ||
| 438 | } | ||
| 439 | |||
| 392 | bo = host1x_bo_lookup(file, cmdbuf.handle); | 440 | bo = host1x_bo_lookup(file, cmdbuf.handle); |
| 393 | if (!bo) { | 441 | if (!bo) { |
| 394 | err = -ENOENT; | 442 | err = -ENOENT; |
| 395 | goto fail; | 443 | goto fail; |
| 396 | } | 444 | } |
| 397 | 445 | ||
| 446 | offset = (u64)cmdbuf.offset + (u64)cmdbuf.words * sizeof(u32); | ||
| 447 | obj = host1x_to_tegra_bo(bo); | ||
| 448 | |||
| 449 | /* | ||
| 450 | * Gather buffer base address must be 4-bytes aligned, | ||
| 451 | * unaligned offset is malformed and cause commands stream | ||
| 452 | * corruption on the buffer address relocation. | ||
| 453 | */ | ||
| 454 | if (offset & 3 || offset >= obj->gem.size) { | ||
| 455 | err = -EINVAL; | ||
| 456 | goto fail; | ||
| 457 | } | ||
| 458 | |||
| 398 | host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset); | 459 | host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset); |
| 399 | num_cmdbufs--; | 460 | num_cmdbufs--; |
| 400 | cmdbufs++; | 461 | cmdbufs++; |
| @@ -402,17 +463,59 @@ int tegra_drm_submit(struct tegra_drm_context *context, | |||
| 402 | 463 | ||
| 403 | /* copy and resolve relocations from submit */ | 464 | /* copy and resolve relocations from submit */ |
| 404 | while (num_relocs--) { | 465 | while (num_relocs--) { |
| 466 | struct host1x_reloc *reloc; | ||
| 467 | struct tegra_bo *obj; | ||
| 468 | |||
| 405 | err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs], | 469 | err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs], |
| 406 | &relocs[num_relocs], drm, | 470 | &relocs[num_relocs], drm, |
| 407 | file); | 471 | file); |
| 408 | if (err < 0) | 472 | if (err < 0) |
| 409 | goto fail; | 473 | goto fail; |
| 474 | |||
| 475 | reloc = &job->relocarray[num_relocs]; | ||
| 476 | obj = host1x_to_tegra_bo(reloc->cmdbuf.bo); | ||
| 477 | |||
| 478 | /* | ||
| 479 | * The unaligned cmdbuf offset will cause an unaligned write | ||
| 480 | * during of the relocations patching, corrupting the commands | ||
| 481 | * stream. | ||
| 482 | */ | ||
| 483 | if (reloc->cmdbuf.offset & 3 || | ||
| 484 | reloc->cmdbuf.offset >= obj->gem.size) { | ||
| 485 | err = -EINVAL; | ||
| 486 | goto fail; | ||
| 487 | } | ||
| 488 | |||
| 489 | obj = host1x_to_tegra_bo(reloc->target.bo); | ||
| 490 | |||
| 491 | if (reloc->target.offset >= obj->gem.size) { | ||
| 492 | err = -EINVAL; | ||
| 493 | goto fail; | ||
| 494 | } | ||
| 410 | } | 495 | } |
| 411 | 496 | ||
| 412 | if (copy_from_user(job->waitchk, waitchks, | 497 | /* copy and resolve waitchks from submit */ |
| 413 | sizeof(*waitchks) * num_waitchks)) { | 498 | while (num_waitchks--) { |
| 414 | err = -EFAULT; | 499 | struct host1x_waitchk *wait = &job->waitchk[num_waitchks]; |
| 415 | goto fail; | 500 | struct tegra_bo *obj; |
| 501 | |||
| 502 | err = host1x_waitchk_copy_from_user(wait, | ||
| 503 | &waitchks[num_waitchks], | ||
| 504 | file); | ||
| 505 | if (err < 0) | ||
| 506 | goto fail; | ||
| 507 | |||
| 508 | obj = host1x_to_tegra_bo(wait->bo); | ||
| 509 | |||
| 510 | /* | ||
| 511 | * The unaligned offset will cause an unaligned write during | ||
| 512 | * of the waitchks patching, corrupting the commands stream. | ||
| 513 | */ | ||
| 514 | if (wait->offset & 3 || | ||
| 515 | wait->offset >= obj->gem.size) { | ||
| 516 | err = -EINVAL; | ||
| 517 | goto fail; | ||
| 518 | } | ||
| 416 | } | 519 | } |
| 417 | 520 | ||
| 418 | if (copy_from_user(&syncpt, (void __user *)(uintptr_t)args->syncpts, | 521 | if (copy_from_user(&syncpt, (void __user *)(uintptr_t)args->syncpts, |
| @@ -421,7 +524,15 @@ int tegra_drm_submit(struct tegra_drm_context *context, | |||
| 421 | goto fail; | 524 | goto fail; |
| 422 | } | 525 | } |
| 423 | 526 | ||
| 527 | /* check whether syncpoint ID is valid */ | ||
| 528 | sp = host1x_syncpt_get(host1x, syncpt.id); | ||
| 529 | if (!sp) { | ||
| 530 | err = -ENOENT; | ||
| 531 | goto fail; | ||
| 532 | } | ||
| 533 | |||
| 424 | job->is_addr_reg = context->client->ops->is_addr_reg; | 534 | job->is_addr_reg = context->client->ops->is_addr_reg; |
| 535 | job->is_valid_class = context->client->ops->is_valid_class; | ||
| 425 | job->syncpt_incrs = syncpt.incrs; | 536 | job->syncpt_incrs = syncpt.incrs; |
| 426 | job->syncpt_id = syncpt.id; | 537 | job->syncpt_id = syncpt.id; |
| 427 | job->timeout = 10000; | 538 | job->timeout = 10000; |
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 85aa2e3d9d4e..6d6da01282f3 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h | |||
| @@ -83,6 +83,7 @@ struct tegra_drm_client_ops { | |||
| 83 | struct tegra_drm_context *context); | 83 | struct tegra_drm_context *context); |
| 84 | void (*close_channel)(struct tegra_drm_context *context); | 84 | void (*close_channel)(struct tegra_drm_context *context); |
| 85 | int (*is_addr_reg)(struct device *dev, u32 class, u32 offset); | 85 | int (*is_addr_reg)(struct device *dev, u32 class, u32 offset); |
| 86 | int (*is_valid_class)(u32 class); | ||
| 86 | int (*submit)(struct tegra_drm_context *context, | 87 | int (*submit)(struct tegra_drm_context *context, |
| 87 | struct drm_tegra_submit *args, struct drm_device *drm, | 88 | struct drm_tegra_submit *args, struct drm_device *drm, |
| 88 | struct drm_file *file); | 89 | struct drm_file *file); |
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 424569b53e57..7a39a355678a 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c | |||
| @@ -20,11 +20,6 @@ | |||
| 20 | #include "drm.h" | 20 | #include "drm.h" |
| 21 | #include "gem.h" | 21 | #include "gem.h" |
| 22 | 22 | ||
| 23 | static inline struct tegra_bo *host1x_to_tegra_bo(struct host1x_bo *bo) | ||
| 24 | { | ||
| 25 | return container_of(bo, struct tegra_bo, base); | ||
| 26 | } | ||
| 27 | |||
| 28 | static void tegra_bo_put(struct host1x_bo *bo) | 23 | static void tegra_bo_put(struct host1x_bo *bo) |
| 29 | { | 24 | { |
| 30 | struct tegra_bo *obj = host1x_to_tegra_bo(bo); | 25 | struct tegra_bo *obj = host1x_to_tegra_bo(bo); |
diff --git a/drivers/gpu/drm/tegra/gem.h b/drivers/gpu/drm/tegra/gem.h index 6c5f12ac0087..8b32a6fd586d 100644 --- a/drivers/gpu/drm/tegra/gem.h +++ b/drivers/gpu/drm/tegra/gem.h | |||
| @@ -52,6 +52,11 @@ static inline struct tegra_bo *to_tegra_bo(struct drm_gem_object *gem) | |||
| 52 | return container_of(gem, struct tegra_bo, gem); | 52 | return container_of(gem, struct tegra_bo, gem); |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | static inline struct tegra_bo *host1x_to_tegra_bo(struct host1x_bo *bo) | ||
| 56 | { | ||
| 57 | return container_of(bo, struct tegra_bo, base); | ||
| 58 | } | ||
| 59 | |||
| 55 | struct tegra_bo *tegra_bo_create(struct drm_device *drm, size_t size, | 60 | struct tegra_bo *tegra_bo_create(struct drm_device *drm, size_t size, |
| 56 | unsigned long flags); | 61 | unsigned long flags); |
| 57 | struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file, | 62 | struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file, |
diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index 02cd3e37a6ec..6ea070da7718 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c | |||
| @@ -38,7 +38,7 @@ static int gr2d_init(struct host1x_client *client) | |||
| 38 | 38 | ||
| 39 | client->syncpts[0] = host1x_syncpt_request(client->dev, flags); | 39 | client->syncpts[0] = host1x_syncpt_request(client->dev, flags); |
| 40 | if (!client->syncpts[0]) { | 40 | if (!client->syncpts[0]) { |
| 41 | host1x_channel_free(gr2d->channel); | 41 | host1x_channel_put(gr2d->channel); |
| 42 | return -ENOMEM; | 42 | return -ENOMEM; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| @@ -57,7 +57,7 @@ static int gr2d_exit(struct host1x_client *client) | |||
| 57 | return err; | 57 | return err; |
| 58 | 58 | ||
| 59 | host1x_syncpt_free(client->syncpts[0]); | 59 | host1x_syncpt_free(client->syncpts[0]); |
| 60 | host1x_channel_free(gr2d->channel); | 60 | host1x_channel_put(gr2d->channel); |
| 61 | 61 | ||
| 62 | return 0; | 62 | return 0; |
| 63 | } | 63 | } |
| @@ -109,10 +109,17 @@ static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 offset) | |||
| 109 | return 0; | 109 | return 0; |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | static int gr2d_is_valid_class(u32 class) | ||
| 113 | { | ||
| 114 | return (class == HOST1X_CLASS_GR2D || | ||
| 115 | class == HOST1X_CLASS_GR2D_SB); | ||
| 116 | } | ||
| 117 | |||
| 112 | static const struct tegra_drm_client_ops gr2d_ops = { | 118 | static const struct tegra_drm_client_ops gr2d_ops = { |
| 113 | .open_channel = gr2d_open_channel, | 119 | .open_channel = gr2d_open_channel, |
| 114 | .close_channel = gr2d_close_channel, | 120 | .close_channel = gr2d_close_channel, |
| 115 | .is_addr_reg = gr2d_is_addr_reg, | 121 | .is_addr_reg = gr2d_is_addr_reg, |
| 122 | .is_valid_class = gr2d_is_valid_class, | ||
| 116 | .submit = tegra_drm_submit, | 123 | .submit = tegra_drm_submit, |
| 117 | }; | 124 | }; |
| 118 | 125 | ||
diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 13f0d1b7cd98..cee2ab645cde 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c | |||
| @@ -48,7 +48,7 @@ static int gr3d_init(struct host1x_client *client) | |||
| 48 | 48 | ||
| 49 | client->syncpts[0] = host1x_syncpt_request(client->dev, flags); | 49 | client->syncpts[0] = host1x_syncpt_request(client->dev, flags); |
| 50 | if (!client->syncpts[0]) { | 50 | if (!client->syncpts[0]) { |
| 51 | host1x_channel_free(gr3d->channel); | 51 | host1x_channel_put(gr3d->channel); |
| 52 | return -ENOMEM; | 52 | return -ENOMEM; |
| 53 | } | 53 | } |
| 54 | 54 | ||
| @@ -67,7 +67,7 @@ static int gr3d_exit(struct host1x_client *client) | |||
| 67 | return err; | 67 | return err; |
| 68 | 68 | ||
| 69 | host1x_syncpt_free(client->syncpts[0]); | 69 | host1x_syncpt_free(client->syncpts[0]); |
| 70 | host1x_channel_free(gr3d->channel); | 70 | host1x_channel_put(gr3d->channel); |
| 71 | 71 | ||
| 72 | return 0; | 72 | return 0; |
| 73 | } | 73 | } |
diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c index cd804e404a11..47cb1aaa58b1 100644 --- a/drivers/gpu/drm/tegra/vic.c +++ b/drivers/gpu/drm/tegra/vic.c | |||
| @@ -182,7 +182,7 @@ static int vic_init(struct host1x_client *client) | |||
| 182 | free_syncpt: | 182 | free_syncpt: |
| 183 | host1x_syncpt_free(client->syncpts[0]); | 183 | host1x_syncpt_free(client->syncpts[0]); |
| 184 | free_channel: | 184 | free_channel: |
| 185 | host1x_channel_free(vic->channel); | 185 | host1x_channel_put(vic->channel); |
| 186 | detach_device: | 186 | detach_device: |
| 187 | if (tegra->domain) | 187 | if (tegra->domain) |
| 188 | iommu_detach_device(tegra->domain, vic->dev); | 188 | iommu_detach_device(tegra->domain, vic->dev); |
| @@ -203,7 +203,7 @@ static int vic_exit(struct host1x_client *client) | |||
| 203 | return err; | 203 | return err; |
| 204 | 204 | ||
| 205 | host1x_syncpt_free(client->syncpts[0]); | 205 | host1x_syncpt_free(client->syncpts[0]); |
| 206 | host1x_channel_free(vic->channel); | 206 | host1x_channel_put(vic->channel); |
| 207 | 207 | ||
| 208 | if (vic->domain) { | 208 | if (vic->domain) { |
| 209 | iommu_detach_device(vic->domain, vic->dev); | 209 | iommu_detach_device(vic->domain, vic->dev); |
diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index 561831e1ae2c..a048e3ac523d 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c | |||
| @@ -40,6 +40,9 @@ struct host1x_subdev { | |||
| 40 | 40 | ||
| 41 | /** | 41 | /** |
| 42 | * host1x_subdev_add() - add a new subdevice with an associated device node | 42 | * host1x_subdev_add() - add a new subdevice with an associated device node |
| 43 | * @device: host1x device to add the subdevice to | ||
| 44 | * @driver: host1x driver | ||
| 45 | * @np: device node | ||
| 43 | */ | 46 | */ |
| 44 | static int host1x_subdev_add(struct host1x_device *device, | 47 | static int host1x_subdev_add(struct host1x_device *device, |
| 45 | struct device_node *np) | 48 | struct device_node *np) |
| @@ -62,6 +65,7 @@ static int host1x_subdev_add(struct host1x_device *device, | |||
| 62 | 65 | ||
| 63 | /** | 66 | /** |
| 64 | * host1x_subdev_del() - remove subdevice | 67 | * host1x_subdev_del() - remove subdevice |
| 68 | * @subdev: subdevice to remove | ||
| 65 | */ | 69 | */ |
| 66 | static void host1x_subdev_del(struct host1x_subdev *subdev) | 70 | static void host1x_subdev_del(struct host1x_subdev *subdev) |
| 67 | { | 71 | { |
| @@ -72,6 +76,8 @@ static void host1x_subdev_del(struct host1x_subdev *subdev) | |||
| 72 | 76 | ||
| 73 | /** | 77 | /** |
| 74 | * host1x_device_parse_dt() - scan device tree and add matching subdevices | 78 | * host1x_device_parse_dt() - scan device tree and add matching subdevices |
| 79 | * @device: host1x logical device | ||
| 80 | * @driver: host1x driver | ||
| 75 | */ | 81 | */ |
| 76 | static int host1x_device_parse_dt(struct host1x_device *device, | 82 | static int host1x_device_parse_dt(struct host1x_device *device, |
| 77 | struct host1x_driver *driver) | 83 | struct host1x_driver *driver) |
| @@ -166,6 +172,16 @@ static void host1x_subdev_unregister(struct host1x_device *device, | |||
| 166 | mutex_unlock(&device->subdevs_lock); | 172 | mutex_unlock(&device->subdevs_lock); |
| 167 | } | 173 | } |
| 168 | 174 | ||
| 175 | /** | ||
| 176 | * host1x_device_init() - initialize a host1x logical device | ||
| 177 | * @device: host1x logical device | ||
| 178 | * | ||
| 179 | * The driver for the host1x logical device can call this during execution of | ||
| 180 | * its &host1x_driver.probe implementation to initialize each of its clients. | ||
| 181 | * The client drivers access the subsystem specific driver data using the | ||
| 182 | * &host1x_client.parent field and driver data associated with it (usually by | ||
| 183 | * calling dev_get_drvdata()). | ||
| 184 | */ | ||
| 169 | int host1x_device_init(struct host1x_device *device) | 185 | int host1x_device_init(struct host1x_device *device) |
| 170 | { | 186 | { |
| 171 | struct host1x_client *client; | 187 | struct host1x_client *client; |
| @@ -192,6 +208,15 @@ int host1x_device_init(struct host1x_device *device) | |||
| 192 | } | 208 | } |
| 193 | EXPORT_SYMBOL(host1x_device_init); | 209 | EXPORT_SYMBOL(host1x_device_init); |
| 194 | 210 | ||
| 211 | /** | ||
| 212 | * host1x_device_exit() - uninitialize host1x logical device | ||
| 213 | * @device: host1x logical device | ||
| 214 | * | ||
| 215 | * When the driver for a host1x logical device is unloaded, it can call this | ||
| 216 | * function to tear down each of its clients. Typically this is done after a | ||
| 217 | * subsystem-specific data structure is removed and the functionality can no | ||
| 218 | * longer be used. | ||
| 219 | */ | ||
| 195 | int host1x_device_exit(struct host1x_device *device) | 220 | int host1x_device_exit(struct host1x_device *device) |
| 196 | { | 221 | { |
| 197 | struct host1x_client *client; | 222 | struct host1x_client *client; |
| @@ -446,6 +471,14 @@ static void host1x_detach_driver(struct host1x *host1x, | |||
| 446 | mutex_unlock(&host1x->devices_lock); | 471 | mutex_unlock(&host1x->devices_lock); |
| 447 | } | 472 | } |
| 448 | 473 | ||
| 474 | /** | ||
| 475 | * host1x_register() - register a host1x controller | ||
| 476 | * @host1x: host1x controller | ||
| 477 | * | ||
| 478 | * The host1x controller driver uses this to register a host1x controller with | ||
| 479 | * the infrastructure. Note that all Tegra SoC generations have only ever come | ||
| 480 | * with a single host1x instance, so this function is somewhat academic. | ||
| 481 | */ | ||
| 449 | int host1x_register(struct host1x *host1x) | 482 | int host1x_register(struct host1x *host1x) |
| 450 | { | 483 | { |
| 451 | struct host1x_driver *driver; | 484 | struct host1x_driver *driver; |
| @@ -464,6 +497,13 @@ int host1x_register(struct host1x *host1x) | |||
| 464 | return 0; | 497 | return 0; |
| 465 | } | 498 | } |
| 466 | 499 | ||
| 500 | /** | ||
| 501 | * host1x_unregister() - unregister a host1x controller | ||
| 502 | * @host1x: host1x controller | ||
| 503 | * | ||
| 504 | * The host1x controller driver uses this to remove a host1x controller from | ||
| 505 | * the infrastructure. | ||
| 506 | */ | ||
| 467 | int host1x_unregister(struct host1x *host1x) | 507 | int host1x_unregister(struct host1x *host1x) |
| 468 | { | 508 | { |
| 469 | struct host1x_driver *driver; | 509 | struct host1x_driver *driver; |
| @@ -513,6 +553,16 @@ static void host1x_device_shutdown(struct device *dev) | |||
| 513 | driver->shutdown(device); | 553 | driver->shutdown(device); |
| 514 | } | 554 | } |
| 515 | 555 | ||
| 556 | /** | ||
| 557 | * host1x_driver_register_full() - register a host1x driver | ||
| 558 | * @driver: host1x driver | ||
| 559 | * @owner: owner module | ||
| 560 | * | ||
| 561 | * Drivers for host1x logical devices call this function to register a driver | ||
| 562 | * with the infrastructure. Note that since these drive logical devices, the | ||
| 563 | * registration of the driver actually triggers tho logical device creation. | ||
| 564 | * A logical device will be created for each host1x instance. | ||
| 565 | */ | ||
| 516 | int host1x_driver_register_full(struct host1x_driver *driver, | 566 | int host1x_driver_register_full(struct host1x_driver *driver, |
| 517 | struct module *owner) | 567 | struct module *owner) |
| 518 | { | 568 | { |
| @@ -541,6 +591,13 @@ int host1x_driver_register_full(struct host1x_driver *driver, | |||
| 541 | } | 591 | } |
| 542 | EXPORT_SYMBOL(host1x_driver_register_full); | 592 | EXPORT_SYMBOL(host1x_driver_register_full); |
| 543 | 593 | ||
| 594 | /** | ||
| 595 | * host1x_driver_unregister() - unregister a host1x driver | ||
| 596 | * @driver: host1x driver | ||
| 597 | * | ||
| 598 | * Unbinds the driver from each of the host1x logical devices that it is | ||
| 599 | * bound to, effectively removing the subsystem devices that they represent. | ||
| 600 | */ | ||
| 544 | void host1x_driver_unregister(struct host1x_driver *driver) | 601 | void host1x_driver_unregister(struct host1x_driver *driver) |
| 545 | { | 602 | { |
| 546 | driver_unregister(&driver->driver); | 603 | driver_unregister(&driver->driver); |
| @@ -551,6 +608,17 @@ void host1x_driver_unregister(struct host1x_driver *driver) | |||
| 551 | } | 608 | } |
| 552 | EXPORT_SYMBOL(host1x_driver_unregister); | 609 | EXPORT_SYMBOL(host1x_driver_unregister); |
| 553 | 610 | ||
| 611 | /** | ||
| 612 | * host1x_client_register() - register a host1x client | ||
| 613 | * @client: host1x client | ||
| 614 | * | ||
| 615 | * Registers a host1x client with each host1x controller instance. Note that | ||
| 616 | * each client will only match their parent host1x controller and will only be | ||
| 617 | * associated with that instance. Once all clients have been registered with | ||
| 618 | * their parent host1x controller, the infrastructure will set up the logical | ||
| 619 | * device and call host1x_device_init(), which will in turn call each client's | ||
| 620 | * &host1x_client_ops.init implementation. | ||
| 621 | */ | ||
| 554 | int host1x_client_register(struct host1x_client *client) | 622 | int host1x_client_register(struct host1x_client *client) |
| 555 | { | 623 | { |
| 556 | struct host1x *host1x; | 624 | struct host1x *host1x; |
| @@ -576,6 +644,13 @@ int host1x_client_register(struct host1x_client *client) | |||
| 576 | } | 644 | } |
| 577 | EXPORT_SYMBOL(host1x_client_register); | 645 | EXPORT_SYMBOL(host1x_client_register); |
| 578 | 646 | ||
| 647 | /** | ||
| 648 | * host1x_client_unregister() - unregister a host1x client | ||
| 649 | * @client: host1x client | ||
| 650 | * | ||
| 651 | * Removes a host1x client from its host1x controller instance. If a logical | ||
| 652 | * device has already been initialized, it will be torn down. | ||
| 653 | */ | ||
| 579 | int host1x_client_unregister(struct host1x_client *client) | 654 | int host1x_client_unregister(struct host1x_client *client) |
| 580 | { | 655 | { |
| 581 | struct host1x_client *c; | 656 | struct host1x_client *c; |
diff --git a/drivers/gpu/host1x/cdma.h b/drivers/gpu/host1x/cdma.h index ec170a78f4e1..286d49386be9 100644 --- a/drivers/gpu/host1x/cdma.h +++ b/drivers/gpu/host1x/cdma.h | |||
| @@ -88,7 +88,6 @@ struct host1x_cdma { | |||
| 88 | 88 | ||
| 89 | int host1x_cdma_init(struct host1x_cdma *cdma); | 89 | int host1x_cdma_init(struct host1x_cdma *cdma); |
| 90 | int host1x_cdma_deinit(struct host1x_cdma *cdma); | 90 | int host1x_cdma_deinit(struct host1x_cdma *cdma); |
| 91 | void host1x_cdma_stop(struct host1x_cdma *cdma); | ||
| 92 | int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job); | 91 | int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job); |
| 93 | void host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2); | 92 | void host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2); |
| 94 | void host1x_cdma_end(struct host1x_cdma *cdma, struct host1x_job *job); | 93 | void host1x_cdma_end(struct host1x_cdma *cdma, struct host1x_job *job); |
diff --git a/drivers/gpu/host1x/channel.c b/drivers/gpu/host1x/channel.c index 8f437d924c10..db9b91d1384c 100644 --- a/drivers/gpu/host1x/channel.c +++ b/drivers/gpu/host1x/channel.c | |||
| @@ -24,19 +24,33 @@ | |||
| 24 | #include "job.h" | 24 | #include "job.h" |
| 25 | 25 | ||
| 26 | /* Constructor for the host1x device list */ | 26 | /* Constructor for the host1x device list */ |
| 27 | int host1x_channel_list_init(struct host1x *host) | 27 | int host1x_channel_list_init(struct host1x_channel_list *chlist, |
| 28 | unsigned int num_channels) | ||
| 28 | { | 29 | { |
| 29 | INIT_LIST_HEAD(&host->chlist.list); | 30 | chlist->channels = kcalloc(num_channels, sizeof(struct host1x_channel), |
| 30 | mutex_init(&host->chlist_mutex); | 31 | GFP_KERNEL); |
| 31 | 32 | if (!chlist->channels) | |
| 32 | if (host->info->nb_channels > BITS_PER_LONG) { | 33 | return -ENOMEM; |
| 33 | WARN(1, "host1x hardware has more channels than supported by the driver\n"); | 34 | |
| 34 | return -ENOSYS; | 35 | chlist->allocated_channels = |
| 36 | kcalloc(BITS_TO_LONGS(num_channels), sizeof(unsigned long), | ||
| 37 | GFP_KERNEL); | ||
| 38 | if (!chlist->allocated_channels) { | ||
| 39 | kfree(chlist->channels); | ||
| 40 | return -ENOMEM; | ||
| 35 | } | 41 | } |
| 36 | 42 | ||
| 43 | bitmap_zero(chlist->allocated_channels, num_channels); | ||
| 44 | |||
| 37 | return 0; | 45 | return 0; |
| 38 | } | 46 | } |
| 39 | 47 | ||
| 48 | void host1x_channel_list_free(struct host1x_channel_list *chlist) | ||
| 49 | { | ||
| 50 | kfree(chlist->allocated_channels); | ||
| 51 | kfree(chlist->channels); | ||
| 52 | } | ||
| 53 | |||
| 40 | int host1x_job_submit(struct host1x_job *job) | 54 | int host1x_job_submit(struct host1x_job *job) |
| 41 | { | 55 | { |
| 42 | struct host1x *host = dev_get_drvdata(job->channel->dev->parent); | 56 | struct host1x *host = dev_get_drvdata(job->channel->dev->parent); |
| @@ -47,86 +61,107 @@ EXPORT_SYMBOL(host1x_job_submit); | |||
| 47 | 61 | ||
| 48 | struct host1x_channel *host1x_channel_get(struct host1x_channel *channel) | 62 | struct host1x_channel *host1x_channel_get(struct host1x_channel *channel) |
| 49 | { | 63 | { |
| 50 | int err = 0; | 64 | kref_get(&channel->refcount); |
| 51 | 65 | ||
| 52 | mutex_lock(&channel->reflock); | 66 | return channel; |
| 67 | } | ||
| 68 | EXPORT_SYMBOL(host1x_channel_get); | ||
| 53 | 69 | ||
| 54 | if (channel->refcount == 0) | 70 | /** |
| 55 | err = host1x_cdma_init(&channel->cdma); | 71 | * host1x_channel_get_index() - Attempt to get channel reference by index |
| 72 | * @host: Host1x device object | ||
| 73 | * @index: Index of channel | ||
| 74 | * | ||
| 75 | * If channel number @index is currently allocated, increase its refcount | ||
| 76 | * and return a pointer to it. Otherwise, return NULL. | ||
| 77 | */ | ||
| 78 | struct host1x_channel *host1x_channel_get_index(struct host1x *host, | ||
| 79 | unsigned int index) | ||
| 80 | { | ||
| 81 | struct host1x_channel *ch = &host->channel_list.channels[index]; | ||
| 56 | 82 | ||
| 57 | if (!err) | 83 | if (!kref_get_unless_zero(&ch->refcount)) |
| 58 | channel->refcount++; | 84 | return NULL; |
| 59 | 85 | ||
| 60 | mutex_unlock(&channel->reflock); | 86 | return ch; |
| 87 | } | ||
| 88 | |||
| 89 | static void release_channel(struct kref *kref) | ||
| 90 | { | ||
| 91 | struct host1x_channel *channel = | ||
| 92 | container_of(kref, struct host1x_channel, refcount); | ||
| 93 | struct host1x *host = dev_get_drvdata(channel->dev->parent); | ||
| 94 | struct host1x_channel_list *chlist = &host->channel_list; | ||
| 95 | |||
| 96 | host1x_hw_cdma_stop(host, &channel->cdma); | ||
| 97 | host1x_cdma_deinit(&channel->cdma); | ||
| 61 | 98 | ||
| 62 | return err ? NULL : channel; | 99 | clear_bit(channel->id, chlist->allocated_channels); |
| 63 | } | 100 | } |
| 64 | EXPORT_SYMBOL(host1x_channel_get); | ||
| 65 | 101 | ||
| 66 | void host1x_channel_put(struct host1x_channel *channel) | 102 | void host1x_channel_put(struct host1x_channel *channel) |
| 67 | { | 103 | { |
| 68 | mutex_lock(&channel->reflock); | 104 | kref_put(&channel->refcount, release_channel); |
| 105 | } | ||
| 106 | EXPORT_SYMBOL(host1x_channel_put); | ||
| 69 | 107 | ||
| 70 | if (channel->refcount == 1) { | 108 | static struct host1x_channel *acquire_unused_channel(struct host1x *host) |
| 71 | struct host1x *host = dev_get_drvdata(channel->dev->parent); | 109 | { |
| 110 | struct host1x_channel_list *chlist = &host->channel_list; | ||
| 111 | unsigned int max_channels = host->info->nb_channels; | ||
| 112 | unsigned int index; | ||
| 72 | 113 | ||
| 73 | host1x_hw_cdma_stop(host, &channel->cdma); | 114 | index = find_first_zero_bit(chlist->allocated_channels, max_channels); |
| 74 | host1x_cdma_deinit(&channel->cdma); | 115 | if (index >= max_channels) { |
| 116 | dev_err(host->dev, "failed to find free channel\n"); | ||
| 117 | return NULL; | ||
| 75 | } | 118 | } |
| 76 | 119 | ||
| 77 | channel->refcount--; | 120 | chlist->channels[index].id = index; |
| 78 | 121 | ||
| 79 | mutex_unlock(&channel->reflock); | 122 | set_bit(index, chlist->allocated_channels); |
| 123 | |||
| 124 | return &chlist->channels[index]; | ||
| 80 | } | 125 | } |
| 81 | EXPORT_SYMBOL(host1x_channel_put); | ||
| 82 | 126 | ||
| 127 | /** | ||
| 128 | * host1x_channel_request() - Allocate a channel | ||
| 129 | * @device: Host1x unit this channel will be used to send commands to | ||
| 130 | * | ||
| 131 | * Allocates a new host1x channel for @device. If there are no free channels, | ||
| 132 | * this will sleep until one becomes available. May return NULL if CDMA | ||
| 133 | * initialization fails. | ||
| 134 | */ | ||
| 83 | struct host1x_channel *host1x_channel_request(struct device *dev) | 135 | struct host1x_channel *host1x_channel_request(struct device *dev) |
| 84 | { | 136 | { |
| 85 | struct host1x *host = dev_get_drvdata(dev->parent); | 137 | struct host1x *host = dev_get_drvdata(dev->parent); |
| 86 | unsigned int max_channels = host->info->nb_channels; | 138 | struct host1x_channel_list *chlist = &host->channel_list; |
| 87 | struct host1x_channel *channel = NULL; | 139 | struct host1x_channel *channel; |
| 88 | unsigned long index; | ||
| 89 | int err; | 140 | int err; |
| 90 | 141 | ||
| 91 | mutex_lock(&host->chlist_mutex); | 142 | channel = acquire_unused_channel(host); |
| 143 | if (!channel) | ||
| 144 | return NULL; | ||
| 92 | 145 | ||
| 93 | index = find_first_zero_bit(&host->allocated_channels, max_channels); | 146 | kref_init(&channel->refcount); |
| 94 | if (index >= max_channels) | 147 | mutex_init(&channel->submitlock); |
| 95 | goto fail; | 148 | channel->dev = dev; |
| 96 | 149 | ||
| 97 | channel = kzalloc(sizeof(*channel), GFP_KERNEL); | 150 | err = host1x_hw_channel_init(host, channel, channel->id); |
| 98 | if (!channel) | 151 | if (err < 0) |
| 99 | goto fail; | 152 | goto fail; |
| 100 | 153 | ||
| 101 | err = host1x_hw_channel_init(host, channel, index); | 154 | err = host1x_cdma_init(&channel->cdma); |
| 102 | if (err < 0) | 155 | if (err < 0) |
| 103 | goto fail; | 156 | goto fail; |
| 104 | 157 | ||
| 105 | /* Link device to host1x_channel */ | ||
| 106 | channel->dev = dev; | ||
| 107 | |||
| 108 | /* Add to channel list */ | ||
| 109 | list_add_tail(&channel->list, &host->chlist.list); | ||
| 110 | |||
| 111 | host->allocated_channels |= BIT(index); | ||
| 112 | |||
| 113 | mutex_unlock(&host->chlist_mutex); | ||
| 114 | return channel; | 158 | return channel; |
| 115 | 159 | ||
| 116 | fail: | 160 | fail: |
| 117 | dev_err(dev, "failed to init channel\n"); | 161 | clear_bit(channel->id, chlist->allocated_channels); |
| 118 | kfree(channel); | ||
| 119 | mutex_unlock(&host->chlist_mutex); | ||
| 120 | return NULL; | ||
| 121 | } | ||
| 122 | EXPORT_SYMBOL(host1x_channel_request); | ||
| 123 | 162 | ||
| 124 | void host1x_channel_free(struct host1x_channel *channel) | 163 | dev_err(dev, "failed to initialize channel\n"); |
| 125 | { | ||
| 126 | struct host1x *host = dev_get_drvdata(channel->dev->parent); | ||
| 127 | 164 | ||
| 128 | host->allocated_channels &= ~BIT(channel->id); | 165 | return NULL; |
| 129 | list_del(&channel->list); | ||
| 130 | kfree(channel); | ||
| 131 | } | 166 | } |
| 132 | EXPORT_SYMBOL(host1x_channel_free); | 167 | EXPORT_SYMBOL(host1x_channel_request); |
diff --git a/drivers/gpu/host1x/channel.h b/drivers/gpu/host1x/channel.h index df767cf90d51..7068e42d42df 100644 --- a/drivers/gpu/host1x/channel.h +++ b/drivers/gpu/host1x/channel.h | |||
| @@ -20,17 +20,21 @@ | |||
| 20 | #define __HOST1X_CHANNEL_H | 20 | #define __HOST1X_CHANNEL_H |
| 21 | 21 | ||
| 22 | #include <linux/io.h> | 22 | #include <linux/io.h> |
| 23 | #include <linux/kref.h> | ||
| 23 | 24 | ||
| 24 | #include "cdma.h" | 25 | #include "cdma.h" |
| 25 | 26 | ||
| 26 | struct host1x; | 27 | struct host1x; |
| 28 | struct host1x_channel; | ||
| 27 | 29 | ||
| 28 | struct host1x_channel { | 30 | struct host1x_channel_list { |
| 29 | struct list_head list; | 31 | struct host1x_channel *channels; |
| 32 | unsigned long *allocated_channels; | ||
| 33 | }; | ||
| 30 | 34 | ||
| 31 | unsigned int refcount; | 35 | struct host1x_channel { |
| 36 | struct kref refcount; | ||
| 32 | unsigned int id; | 37 | unsigned int id; |
| 33 | struct mutex reflock; | ||
| 34 | struct mutex submitlock; | 38 | struct mutex submitlock; |
| 35 | void __iomem *regs; | 39 | void __iomem *regs; |
| 36 | struct device *dev; | 40 | struct device *dev; |
| @@ -38,9 +42,10 @@ struct host1x_channel { | |||
| 38 | }; | 42 | }; |
| 39 | 43 | ||
| 40 | /* channel list operations */ | 44 | /* channel list operations */ |
| 41 | int host1x_channel_list_init(struct host1x *host); | 45 | int host1x_channel_list_init(struct host1x_channel_list *chlist, |
| 42 | 46 | unsigned int num_channels); | |
| 43 | #define host1x_for_each_channel(host, channel) \ | 47 | void host1x_channel_list_free(struct host1x_channel_list *chlist); |
| 44 | list_for_each_entry(channel, &host->chlist.list, list) | 48 | struct host1x_channel *host1x_channel_get_index(struct host1x *host, |
| 49 | unsigned int index); | ||
| 45 | 50 | ||
| 46 | #endif | 51 | #endif |
diff --git a/drivers/gpu/host1x/debug.c b/drivers/gpu/host1x/debug.c index d9330fcc62ad..2aae0e63214c 100644 --- a/drivers/gpu/host1x/debug.c +++ b/drivers/gpu/host1x/debug.c | |||
| @@ -43,24 +43,19 @@ void host1x_debug_output(struct output *o, const char *fmt, ...) | |||
| 43 | o->fn(o->ctx, o->buf, len); | 43 | o->fn(o->ctx, o->buf, len); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | static int show_channels(struct host1x_channel *ch, void *data, bool show_fifo) | 46 | static int show_channel(struct host1x_channel *ch, void *data, bool show_fifo) |
| 47 | { | 47 | { |
| 48 | struct host1x *m = dev_get_drvdata(ch->dev->parent); | 48 | struct host1x *m = dev_get_drvdata(ch->dev->parent); |
| 49 | struct output *o = data; | 49 | struct output *o = data; |
| 50 | 50 | ||
| 51 | mutex_lock(&ch->reflock); | 51 | mutex_lock(&ch->cdma.lock); |
| 52 | 52 | ||
| 53 | if (ch->refcount) { | 53 | if (show_fifo) |
| 54 | mutex_lock(&ch->cdma.lock); | 54 | host1x_hw_show_channel_fifo(m, ch, o); |
| 55 | 55 | ||
| 56 | if (show_fifo) | 56 | host1x_hw_show_channel_cdma(m, ch, o); |
| 57 | host1x_hw_show_channel_fifo(m, ch, o); | ||
| 58 | 57 | ||
| 59 | host1x_hw_show_channel_cdma(m, ch, o); | 58 | mutex_unlock(&ch->cdma.lock); |
| 60 | mutex_unlock(&ch->cdma.lock); | ||
| 61 | } | ||
| 62 | |||
| 63 | mutex_unlock(&ch->reflock); | ||
| 64 | 59 | ||
| 65 | return 0; | 60 | return 0; |
| 66 | } | 61 | } |
| @@ -94,28 +89,22 @@ static void show_syncpts(struct host1x *m, struct output *o) | |||
| 94 | host1x_debug_output(o, "\n"); | 89 | host1x_debug_output(o, "\n"); |
| 95 | } | 90 | } |
| 96 | 91 | ||
| 97 | static void show_all(struct host1x *m, struct output *o) | 92 | static void show_all(struct host1x *m, struct output *o, bool show_fifo) |
| 98 | { | 93 | { |
| 99 | struct host1x_channel *ch; | 94 | int i; |
| 100 | 95 | ||
| 101 | host1x_hw_show_mlocks(m, o); | 96 | host1x_hw_show_mlocks(m, o); |
| 102 | show_syncpts(m, o); | 97 | show_syncpts(m, o); |
| 103 | host1x_debug_output(o, "---- channels ----\n"); | 98 | host1x_debug_output(o, "---- channels ----\n"); |
| 104 | 99 | ||
| 105 | host1x_for_each_channel(m, ch) | 100 | for (i = 0; i < m->info->nb_channels; ++i) { |
| 106 | show_channels(ch, o, true); | 101 | struct host1x_channel *ch = host1x_channel_get_index(m, i); |
| 107 | } | ||
| 108 | |||
| 109 | static void show_all_no_fifo(struct host1x *host1x, struct output *o) | ||
| 110 | { | ||
| 111 | struct host1x_channel *ch; | ||
| 112 | |||
| 113 | host1x_hw_show_mlocks(host1x, o); | ||
| 114 | show_syncpts(host1x, o); | ||
| 115 | host1x_debug_output(o, "---- channels ----\n"); | ||
| 116 | 102 | ||
| 117 | host1x_for_each_channel(host1x, ch) | 103 | if (ch) { |
| 118 | show_channels(ch, o, false); | 104 | show_channel(ch, o, show_fifo); |
| 105 | host1x_channel_put(ch); | ||
| 106 | } | ||
| 107 | } | ||
| 119 | } | 108 | } |
| 120 | 109 | ||
| 121 | static int host1x_debug_show_all(struct seq_file *s, void *unused) | 110 | static int host1x_debug_show_all(struct seq_file *s, void *unused) |
| @@ -125,7 +114,7 @@ static int host1x_debug_show_all(struct seq_file *s, void *unused) | |||
| 125 | .ctx = s | 114 | .ctx = s |
| 126 | }; | 115 | }; |
| 127 | 116 | ||
| 128 | show_all(s->private, &o); | 117 | show_all(s->private, &o, true); |
| 129 | 118 | ||
| 130 | return 0; | 119 | return 0; |
| 131 | } | 120 | } |
| @@ -137,7 +126,7 @@ static int host1x_debug_show(struct seq_file *s, void *unused) | |||
| 137 | .ctx = s | 126 | .ctx = s |
| 138 | }; | 127 | }; |
| 139 | 128 | ||
| 140 | show_all_no_fifo(s->private, &o); | 129 | show_all(s->private, &o, false); |
| 141 | 130 | ||
| 142 | return 0; | 131 | return 0; |
| 143 | } | 132 | } |
| @@ -216,7 +205,7 @@ void host1x_debug_dump(struct host1x *host1x) | |||
| 216 | .fn = write_to_printk | 205 | .fn = write_to_printk |
| 217 | }; | 206 | }; |
| 218 | 207 | ||
| 219 | show_all(host1x, &o); | 208 | show_all(host1x, &o, true); |
| 220 | } | 209 | } |
| 221 | 210 | ||
| 222 | void host1x_debug_dump_syncpts(struct host1x *host1x) | 211 | void host1x_debug_dump_syncpts(struct host1x *host1x) |
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index f05ebb14fa63..5c1c711a21af 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c | |||
| @@ -198,7 +198,8 @@ static int host1x_probe(struct platform_device *pdev) | |||
| 198 | host->iova_end = geometry->aperture_end; | 198 | host->iova_end = geometry->aperture_end; |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | err = host1x_channel_list_init(host); | 201 | err = host1x_channel_list_init(&host->channel_list, |
| 202 | host->info->nb_channels); | ||
| 202 | if (err) { | 203 | if (err) { |
| 203 | dev_err(&pdev->dev, "failed to initialize channel list\n"); | 204 | dev_err(&pdev->dev, "failed to initialize channel list\n"); |
| 204 | goto fail_detach_device; | 205 | goto fail_detach_device; |
| @@ -207,7 +208,7 @@ static int host1x_probe(struct platform_device *pdev) | |||
| 207 | err = clk_prepare_enable(host->clk); | 208 | err = clk_prepare_enable(host->clk); |
| 208 | if (err < 0) { | 209 | if (err < 0) { |
| 209 | dev_err(&pdev->dev, "failed to enable clock\n"); | 210 | dev_err(&pdev->dev, "failed to enable clock\n"); |
| 210 | goto fail_detach_device; | 211 | goto fail_free_channels; |
| 211 | } | 212 | } |
| 212 | 213 | ||
| 213 | err = reset_control_deassert(host->rst); | 214 | err = reset_control_deassert(host->rst); |
| @@ -244,6 +245,8 @@ fail_reset_assert: | |||
| 244 | reset_control_assert(host->rst); | 245 | reset_control_assert(host->rst); |
| 245 | fail_unprepare_disable: | 246 | fail_unprepare_disable: |
| 246 | clk_disable_unprepare(host->clk); | 247 | clk_disable_unprepare(host->clk); |
| 248 | fail_free_channels: | ||
| 249 | host1x_channel_list_free(&host->channel_list); | ||
| 247 | fail_detach_device: | 250 | fail_detach_device: |
| 248 | if (host->domain) { | 251 | if (host->domain) { |
| 249 | put_iova_domain(&host->iova); | 252 | put_iova_domain(&host->iova); |
diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h index 229d08b6a45e..ffdbc15b749b 100644 --- a/drivers/gpu/host1x/dev.h +++ b/drivers/gpu/host1x/dev.h | |||
| @@ -129,10 +129,8 @@ struct host1x { | |||
| 129 | struct host1x_syncpt *nop_sp; | 129 | struct host1x_syncpt *nop_sp; |
| 130 | 130 | ||
| 131 | struct mutex syncpt_mutex; | 131 | struct mutex syncpt_mutex; |
| 132 | struct mutex chlist_mutex; | 132 | |
| 133 | struct host1x_channel chlist; | 133 | struct host1x_channel_list channel_list; |
| 134 | unsigned long allocated_channels; | ||
| 135 | unsigned int num_allocated_channels; | ||
| 136 | 134 | ||
| 137 | struct dentry *debugfs; | 135 | struct dentry *debugfs; |
| 138 | 136 | ||
diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c index 5e8df78b7acd..8447a56c41ca 100644 --- a/drivers/gpu/host1x/hw/channel_hw.c +++ b/drivers/gpu/host1x/hw/channel_hw.c | |||
| @@ -181,10 +181,6 @@ error: | |||
| 181 | static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev, | 181 | static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev, |
| 182 | unsigned int index) | 182 | unsigned int index) |
| 183 | { | 183 | { |
| 184 | ch->id = index; | ||
| 185 | mutex_init(&ch->reflock); | ||
| 186 | mutex_init(&ch->submitlock); | ||
| 187 | |||
| 188 | ch->regs = dev->regs + index * HOST1X_CHANNEL_SIZE; | 184 | ch->regs = dev->regs + index * HOST1X_CHANNEL_SIZE; |
| 189 | return 0; | 185 | return 0; |
| 190 | } | 186 | } |
diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c index 5f5f8ee6143d..bee504406cfc 100644 --- a/drivers/gpu/host1x/job.c +++ b/drivers/gpu/host1x/job.c | |||
| @@ -31,6 +31,8 @@ | |||
| 31 | #include "job.h" | 31 | #include "job.h" |
| 32 | #include "syncpt.h" | 32 | #include "syncpt.h" |
| 33 | 33 | ||
| 34 | #define HOST1X_WAIT_SYNCPT_OFFSET 0x8 | ||
| 35 | |||
| 34 | struct host1x_job *host1x_job_alloc(struct host1x_channel *ch, | 36 | struct host1x_job *host1x_job_alloc(struct host1x_channel *ch, |
| 35 | u32 num_cmdbufs, u32 num_relocs, | 37 | u32 num_cmdbufs, u32 num_relocs, |
| 36 | u32 num_waitchks) | 38 | u32 num_waitchks) |
| @@ -137,8 +139,9 @@ static void host1x_syncpt_patch_offset(struct host1x_syncpt *sp, | |||
| 137 | * avoid a wrap condition in the HW). | 139 | * avoid a wrap condition in the HW). |
| 138 | */ | 140 | */ |
| 139 | static int do_waitchks(struct host1x_job *job, struct host1x *host, | 141 | static int do_waitchks(struct host1x_job *job, struct host1x *host, |
| 140 | struct host1x_bo *patch) | 142 | struct host1x_job_gather *g) |
| 141 | { | 143 | { |
| 144 | struct host1x_bo *patch = g->bo; | ||
| 142 | int i; | 145 | int i; |
| 143 | 146 | ||
| 144 | /* compare syncpt vs wait threshold */ | 147 | /* compare syncpt vs wait threshold */ |
| @@ -165,7 +168,8 @@ static int do_waitchks(struct host1x_job *job, struct host1x *host, | |||
| 165 | wait->syncpt_id, sp->name, wait->thresh, | 168 | wait->syncpt_id, sp->name, wait->thresh, |
| 166 | host1x_syncpt_read_min(sp)); | 169 | host1x_syncpt_read_min(sp)); |
| 167 | 170 | ||
| 168 | host1x_syncpt_patch_offset(sp, patch, wait->offset); | 171 | host1x_syncpt_patch_offset(sp, patch, |
| 172 | g->offset + wait->offset); | ||
| 169 | } | 173 | } |
| 170 | 174 | ||
| 171 | wait->bo = NULL; | 175 | wait->bo = NULL; |
| @@ -269,11 +273,12 @@ unpin: | |||
| 269 | return err; | 273 | return err; |
| 270 | } | 274 | } |
| 271 | 275 | ||
| 272 | static int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf) | 276 | static int do_relocs(struct host1x_job *job, struct host1x_job_gather *g) |
| 273 | { | 277 | { |
| 274 | int i = 0; | 278 | int i = 0; |
| 275 | u32 last_page = ~0; | 279 | u32 last_page = ~0; |
| 276 | void *cmdbuf_page_addr = NULL; | 280 | void *cmdbuf_page_addr = NULL; |
| 281 | struct host1x_bo *cmdbuf = g->bo; | ||
| 277 | 282 | ||
| 278 | /* pin & patch the relocs for one gather */ | 283 | /* pin & patch the relocs for one gather */ |
| 279 | for (i = 0; i < job->num_relocs; i++) { | 284 | for (i = 0; i < job->num_relocs; i++) { |
| @@ -286,6 +291,13 @@ static int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf) | |||
| 286 | if (cmdbuf != reloc->cmdbuf.bo) | 291 | if (cmdbuf != reloc->cmdbuf.bo) |
| 287 | continue; | 292 | continue; |
| 288 | 293 | ||
| 294 | if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) { | ||
| 295 | target = (u32 *)job->gather_copy_mapped + | ||
| 296 | reloc->cmdbuf.offset / sizeof(u32) + | ||
| 297 | g->offset / sizeof(u32); | ||
| 298 | goto patch_reloc; | ||
| 299 | } | ||
| 300 | |||
| 289 | if (last_page != reloc->cmdbuf.offset >> PAGE_SHIFT) { | 301 | if (last_page != reloc->cmdbuf.offset >> PAGE_SHIFT) { |
| 290 | if (cmdbuf_page_addr) | 302 | if (cmdbuf_page_addr) |
| 291 | host1x_bo_kunmap(cmdbuf, last_page, | 303 | host1x_bo_kunmap(cmdbuf, last_page, |
| @@ -302,6 +314,7 @@ static int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf) | |||
| 302 | } | 314 | } |
| 303 | 315 | ||
| 304 | target = cmdbuf_page_addr + (reloc->cmdbuf.offset & ~PAGE_MASK); | 316 | target = cmdbuf_page_addr + (reloc->cmdbuf.offset & ~PAGE_MASK); |
| 317 | patch_reloc: | ||
| 305 | *target = reloc_addr; | 318 | *target = reloc_addr; |
| 306 | } | 319 | } |
| 307 | 320 | ||
| @@ -319,6 +332,21 @@ static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf, | |||
| 319 | if (reloc->cmdbuf.bo != cmdbuf || reloc->cmdbuf.offset != offset) | 332 | if (reloc->cmdbuf.bo != cmdbuf || reloc->cmdbuf.offset != offset) |
| 320 | return false; | 333 | return false; |
| 321 | 334 | ||
| 335 | /* relocation shift value validation isn't implemented yet */ | ||
| 336 | if (reloc->shift) | ||
| 337 | return false; | ||
| 338 | |||
| 339 | return true; | ||
| 340 | } | ||
| 341 | |||
| 342 | static bool check_wait(struct host1x_waitchk *wait, struct host1x_bo *cmdbuf, | ||
| 343 | unsigned int offset) | ||
| 344 | { | ||
| 345 | offset *= sizeof(u32); | ||
| 346 | |||
| 347 | if (wait->bo != cmdbuf || wait->offset != offset) | ||
| 348 | return false; | ||
| 349 | |||
| 322 | return true; | 350 | return true; |
| 323 | } | 351 | } |
| 324 | 352 | ||
| @@ -329,6 +357,9 @@ struct host1x_firewall { | |||
| 329 | unsigned int num_relocs; | 357 | unsigned int num_relocs; |
| 330 | struct host1x_reloc *reloc; | 358 | struct host1x_reloc *reloc; |
| 331 | 359 | ||
| 360 | unsigned int num_waitchks; | ||
| 361 | struct host1x_waitchk *waitchk; | ||
| 362 | |||
| 332 | struct host1x_bo *cmdbuf; | 363 | struct host1x_bo *cmdbuf; |
| 333 | unsigned int offset; | 364 | unsigned int offset; |
| 334 | 365 | ||
| @@ -341,6 +372,9 @@ struct host1x_firewall { | |||
| 341 | 372 | ||
| 342 | static int check_register(struct host1x_firewall *fw, unsigned long offset) | 373 | static int check_register(struct host1x_firewall *fw, unsigned long offset) |
| 343 | { | 374 | { |
| 375 | if (!fw->job->is_addr_reg) | ||
| 376 | return 0; | ||
| 377 | |||
| 344 | if (fw->job->is_addr_reg(fw->dev, fw->class, offset)) { | 378 | if (fw->job->is_addr_reg(fw->dev, fw->class, offset)) { |
| 345 | if (!fw->num_relocs) | 379 | if (!fw->num_relocs) |
| 346 | return -EINVAL; | 380 | return -EINVAL; |
| @@ -352,6 +386,33 @@ static int check_register(struct host1x_firewall *fw, unsigned long offset) | |||
| 352 | fw->reloc++; | 386 | fw->reloc++; |
| 353 | } | 387 | } |
| 354 | 388 | ||
| 389 | if (offset == HOST1X_WAIT_SYNCPT_OFFSET) { | ||
| 390 | if (fw->class != HOST1X_CLASS_HOST1X) | ||
| 391 | return -EINVAL; | ||
| 392 | |||
| 393 | if (!fw->num_waitchks) | ||
| 394 | return -EINVAL; | ||
| 395 | |||
| 396 | if (!check_wait(fw->waitchk, fw->cmdbuf, fw->offset)) | ||
| 397 | return -EINVAL; | ||
| 398 | |||
| 399 | fw->num_waitchks--; | ||
| 400 | fw->waitchk++; | ||
| 401 | } | ||
| 402 | |||
| 403 | return 0; | ||
| 404 | } | ||
| 405 | |||
| 406 | static int check_class(struct host1x_firewall *fw, u32 class) | ||
| 407 | { | ||
| 408 | if (!fw->job->is_valid_class) { | ||
| 409 | if (fw->class != class) | ||
| 410 | return -EINVAL; | ||
| 411 | } else { | ||
| 412 | if (!fw->job->is_valid_class(fw->class)) | ||
| 413 | return -EINVAL; | ||
| 414 | } | ||
| 415 | |||
| 355 | return 0; | 416 | return 0; |
| 356 | } | 417 | } |
| 357 | 418 | ||
| @@ -428,11 +489,9 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g) | |||
| 428 | { | 489 | { |
| 429 | u32 *cmdbuf_base = (u32 *)fw->job->gather_copy_mapped + | 490 | u32 *cmdbuf_base = (u32 *)fw->job->gather_copy_mapped + |
| 430 | (g->offset / sizeof(u32)); | 491 | (g->offset / sizeof(u32)); |
| 492 | u32 job_class = fw->class; | ||
| 431 | int err = 0; | 493 | int err = 0; |
| 432 | 494 | ||
| 433 | if (!fw->job->is_addr_reg) | ||
| 434 | return 0; | ||
| 435 | |||
| 436 | fw->words = g->words; | 495 | fw->words = g->words; |
| 437 | fw->cmdbuf = g->bo; | 496 | fw->cmdbuf = g->bo; |
| 438 | fw->offset = 0; | 497 | fw->offset = 0; |
| @@ -452,7 +511,9 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g) | |||
| 452 | fw->class = word >> 6 & 0x3ff; | 511 | fw->class = word >> 6 & 0x3ff; |
| 453 | fw->mask = word & 0x3f; | 512 | fw->mask = word & 0x3f; |
| 454 | fw->reg = word >> 16 & 0xfff; | 513 | fw->reg = word >> 16 & 0xfff; |
| 455 | err = check_mask(fw); | 514 | err = check_class(fw, job_class); |
| 515 | if (!err) | ||
| 516 | err = check_mask(fw); | ||
| 456 | if (err) | 517 | if (err) |
| 457 | goto out; | 518 | goto out; |
| 458 | break; | 519 | break; |
| @@ -480,7 +541,6 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g) | |||
| 480 | goto out; | 541 | goto out; |
| 481 | break; | 542 | break; |
| 482 | case 4: | 543 | case 4: |
| 483 | case 5: | ||
| 484 | case 14: | 544 | case 14: |
| 485 | break; | 545 | break; |
| 486 | default: | 546 | default: |
| @@ -504,7 +564,9 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev) | |||
| 504 | fw.dev = dev; | 564 | fw.dev = dev; |
| 505 | fw.reloc = job->relocarray; | 565 | fw.reloc = job->relocarray; |
| 506 | fw.num_relocs = job->num_relocs; | 566 | fw.num_relocs = job->num_relocs; |
| 507 | fw.class = 0; | 567 | fw.waitchk = job->waitchk; |
| 568 | fw.num_waitchks = job->num_waitchk; | ||
| 569 | fw.class = job->class; | ||
| 508 | 570 | ||
| 509 | for (i = 0; i < job->num_gathers; i++) { | 571 | for (i = 0; i < job->num_gathers; i++) { |
| 510 | struct host1x_job_gather *g = &job->gathers[i]; | 572 | struct host1x_job_gather *g = &job->gathers[i]; |
| @@ -512,12 +574,20 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev) | |||
| 512 | size += g->words * sizeof(u32); | 574 | size += g->words * sizeof(u32); |
| 513 | } | 575 | } |
| 514 | 576 | ||
| 577 | /* | ||
| 578 | * Try a non-blocking allocation from a higher priority pools first, | ||
| 579 | * as awaiting for the allocation here is a major performance hit. | ||
| 580 | */ | ||
| 515 | job->gather_copy_mapped = dma_alloc_wc(dev, size, &job->gather_copy, | 581 | job->gather_copy_mapped = dma_alloc_wc(dev, size, &job->gather_copy, |
| 516 | GFP_KERNEL); | 582 | GFP_NOWAIT); |
| 517 | if (!job->gather_copy_mapped) { | 583 | |
| 518 | job->gather_copy_mapped = NULL; | 584 | /* the higher priority allocation failed, try the generic-blocking */ |
| 585 | if (!job->gather_copy_mapped) | ||
| 586 | job->gather_copy_mapped = dma_alloc_wc(dev, size, | ||
| 587 | &job->gather_copy, | ||
| 588 | GFP_KERNEL); | ||
| 589 | if (!job->gather_copy_mapped) | ||
| 519 | return -ENOMEM; | 590 | return -ENOMEM; |
| 520 | } | ||
| 521 | 591 | ||
| 522 | job->gather_copy_size = size; | 592 | job->gather_copy_size = size; |
| 523 | 593 | ||
| @@ -542,8 +612,8 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev) | |||
| 542 | offset += g->words * sizeof(u32); | 612 | offset += g->words * sizeof(u32); |
| 543 | } | 613 | } |
| 544 | 614 | ||
| 545 | /* No relocs should remain at this point */ | 615 | /* No relocs and waitchks should remain at this point */ |
| 546 | if (fw.num_relocs) | 616 | if (fw.num_relocs || fw.num_waitchks) |
| 547 | return -EINVAL; | 617 | return -EINVAL; |
| 548 | 618 | ||
| 549 | return 0; | 619 | return 0; |
| @@ -573,6 +643,12 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev) | |||
| 573 | if (err) | 643 | if (err) |
| 574 | goto out; | 644 | goto out; |
| 575 | 645 | ||
| 646 | if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) { | ||
| 647 | err = copy_gathers(job, dev); | ||
| 648 | if (err) | ||
| 649 | goto out; | ||
| 650 | } | ||
| 651 | |||
| 576 | /* patch gathers */ | 652 | /* patch gathers */ |
| 577 | for (i = 0; i < job->num_gathers; i++) { | 653 | for (i = 0; i < job->num_gathers; i++) { |
| 578 | struct host1x_job_gather *g = &job->gathers[i]; | 654 | struct host1x_job_gather *g = &job->gathers[i]; |
| @@ -581,7 +657,9 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev) | |||
| 581 | if (g->handled) | 657 | if (g->handled) |
| 582 | continue; | 658 | continue; |
| 583 | 659 | ||
| 584 | g->base = job->gather_addr_phys[i]; | 660 | /* copy_gathers() sets gathers base if firewall is enabled */ |
| 661 | if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) | ||
| 662 | g->base = job->gather_addr_phys[i]; | ||
| 585 | 663 | ||
| 586 | for (j = i + 1; j < job->num_gathers; j++) { | 664 | for (j = i + 1; j < job->num_gathers; j++) { |
| 587 | if (job->gathers[j].bo == g->bo) { | 665 | if (job->gathers[j].bo == g->bo) { |
| @@ -590,24 +668,18 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev) | |||
| 590 | } | 668 | } |
| 591 | } | 669 | } |
| 592 | 670 | ||
| 593 | err = do_relocs(job, g->bo); | 671 | err = do_relocs(job, g); |
| 594 | if (err) | 672 | if (err) |
| 595 | break; | 673 | break; |
| 596 | 674 | ||
| 597 | err = do_waitchks(job, host, g->bo); | 675 | err = do_waitchks(job, host, g); |
| 598 | if (err) | 676 | if (err) |
| 599 | break; | 677 | break; |
| 600 | } | 678 | } |
| 601 | 679 | ||
| 602 | if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && !err) { | ||
| 603 | err = copy_gathers(job, dev); | ||
| 604 | if (err) { | ||
| 605 | host1x_job_unpin(job); | ||
| 606 | return err; | ||
| 607 | } | ||
| 608 | } | ||
| 609 | |||
| 610 | out: | 680 | out: |
| 681 | if (err) | ||
| 682 | host1x_job_unpin(job); | ||
| 611 | wmb(); | 683 | wmb(); |
| 612 | 684 | ||
| 613 | return err; | 685 | return err; |
diff --git a/drivers/gpu/host1x/job.h b/drivers/gpu/host1x/job.h index 878239c476d2..4bda51d503ec 100644 --- a/drivers/gpu/host1x/job.h +++ b/drivers/gpu/host1x/job.h | |||
| @@ -27,20 +27,6 @@ struct host1x_job_gather { | |||
| 27 | bool handled; | 27 | bool handled; |
| 28 | }; | 28 | }; |
| 29 | 29 | ||
| 30 | struct host1x_cmdbuf { | ||
| 31 | u32 handle; | ||
| 32 | u32 offset; | ||
| 33 | u32 words; | ||
| 34 | u32 pad; | ||
| 35 | }; | ||
| 36 | |||
| 37 | struct host1x_waitchk { | ||
| 38 | struct host1x_bo *bo; | ||
| 39 | u32 offset; | ||
| 40 | u32 syncpt_id; | ||
| 41 | u32 thresh; | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct host1x_job_unpin_data { | 30 | struct host1x_job_unpin_data { |
| 45 | struct host1x_bo *bo; | 31 | struct host1x_bo *bo; |
| 46 | struct sg_table *sgt; | 32 | struct sg_table *sgt; |
diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c index 0ac026cdc30c..048ac9e344ce 100644 --- a/drivers/gpu/host1x/syncpt.c +++ b/drivers/gpu/host1x/syncpt.c | |||
| @@ -99,14 +99,24 @@ unlock: | |||
| 99 | return NULL; | 99 | return NULL; |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | /** | ||
| 103 | * host1x_syncpt_id() - retrieve syncpoint ID | ||
| 104 | * @sp: host1x syncpoint | ||
| 105 | * | ||
| 106 | * Given a pointer to a struct host1x_syncpt, retrieves its ID. This ID is | ||
| 107 | * often used as a value to program into registers that control how hardware | ||
| 108 | * blocks interact with syncpoints. | ||
| 109 | */ | ||
| 102 | u32 host1x_syncpt_id(struct host1x_syncpt *sp) | 110 | u32 host1x_syncpt_id(struct host1x_syncpt *sp) |
| 103 | { | 111 | { |
| 104 | return sp->id; | 112 | return sp->id; |
| 105 | } | 113 | } |
| 106 | EXPORT_SYMBOL(host1x_syncpt_id); | 114 | EXPORT_SYMBOL(host1x_syncpt_id); |
| 107 | 115 | ||
| 108 | /* | 116 | /** |
| 109 | * Updates the value sent to hardware. | 117 | * host1x_syncpt_incr_max() - update the value sent to hardware |
| 118 | * @sp: host1x syncpoint | ||
| 119 | * @incrs: number of increments | ||
| 110 | */ | 120 | */ |
| 111 | u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs) | 121 | u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs) |
| 112 | { | 122 | { |
| @@ -175,8 +185,9 @@ u32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp) | |||
| 175 | return sp->base_val; | 185 | return sp->base_val; |
| 176 | } | 186 | } |
| 177 | 187 | ||
| 178 | /* | 188 | /** |
| 179 | * Increment syncpoint value from cpu, updating cache | 189 | * host1x_syncpt_incr() - increment syncpoint value from CPU, updating cache |
| 190 | * @sp: host1x syncpoint | ||
| 180 | */ | 191 | */ |
| 181 | int host1x_syncpt_incr(struct host1x_syncpt *sp) | 192 | int host1x_syncpt_incr(struct host1x_syncpt *sp) |
| 182 | { | 193 | { |
| @@ -195,8 +206,12 @@ static bool syncpt_load_min_is_expired(struct host1x_syncpt *sp, u32 thresh) | |||
| 195 | return host1x_syncpt_is_expired(sp, thresh); | 206 | return host1x_syncpt_is_expired(sp, thresh); |
| 196 | } | 207 | } |
| 197 | 208 | ||
| 198 | /* | 209 | /** |
| 199 | * Main entrypoint for syncpoint value waits. | 210 | * host1x_syncpt_wait() - wait for a syncpoint to reach a given value |
| 211 | * @sp: host1x syncpoint | ||
| 212 | * @thresh: threshold | ||
| 213 | * @timeout: maximum time to wait for the syncpoint to reach the given value | ||
| 214 | * @value: return location for the syncpoint value | ||
| 200 | */ | 215 | */ |
| 201 | int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, | 216 | int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, |
| 202 | u32 *value) | 217 | u32 *value) |
| @@ -402,6 +417,16 @@ int host1x_syncpt_init(struct host1x *host) | |||
| 402 | return 0; | 417 | return 0; |
| 403 | } | 418 | } |
| 404 | 419 | ||
| 420 | /** | ||
| 421 | * host1x_syncpt_request() - request a syncpoint | ||
| 422 | * @dev: device requesting the syncpoint | ||
| 423 | * @flags: flags | ||
| 424 | * | ||
| 425 | * host1x client drivers can use this function to allocate a syncpoint for | ||
| 426 | * subsequent use. A syncpoint returned by this function will be reserved for | ||
| 427 | * use by the client exclusively. When no longer using a syncpoint, a host1x | ||
| 428 | * client driver needs to release it using host1x_syncpt_free(). | ||
| 429 | */ | ||
| 405 | struct host1x_syncpt *host1x_syncpt_request(struct device *dev, | 430 | struct host1x_syncpt *host1x_syncpt_request(struct device *dev, |
| 406 | unsigned long flags) | 431 | unsigned long flags) |
| 407 | { | 432 | { |
| @@ -411,6 +436,16 @@ struct host1x_syncpt *host1x_syncpt_request(struct device *dev, | |||
| 411 | } | 436 | } |
| 412 | EXPORT_SYMBOL(host1x_syncpt_request); | 437 | EXPORT_SYMBOL(host1x_syncpt_request); |
| 413 | 438 | ||
| 439 | /** | ||
| 440 | * host1x_syncpt_free() - free a requested syncpoint | ||
| 441 | * @sp: host1x syncpoint | ||
| 442 | * | ||
| 443 | * Release a syncpoint previously allocated using host1x_syncpt_request(). A | ||
| 444 | * host1x client driver should call this when the syncpoint is no longer in | ||
| 445 | * use. Note that client drivers must ensure that the syncpoint doesn't remain | ||
| 446 | * under the control of hardware after calling this function, otherwise two | ||
| 447 | * clients may end up trying to access the same syncpoint concurrently. | ||
| 448 | */ | ||
| 414 | void host1x_syncpt_free(struct host1x_syncpt *sp) | 449 | void host1x_syncpt_free(struct host1x_syncpt *sp) |
| 415 | { | 450 | { |
| 416 | if (!sp) | 451 | if (!sp) |
| @@ -438,9 +473,12 @@ void host1x_syncpt_deinit(struct host1x *host) | |||
| 438 | kfree(sp->name); | 473 | kfree(sp->name); |
| 439 | } | 474 | } |
| 440 | 475 | ||
| 441 | /* | 476 | /** |
| 442 | * Read max. It indicates how many operations there are in queue, either in | 477 | * host1x_syncpt_read_max() - read maximum syncpoint value |
| 443 | * channel or in a software thread. | 478 | * @sp: host1x syncpoint |
| 479 | * | ||
| 480 | * The maximum syncpoint value indicates how many operations there are in | ||
| 481 | * queue, either in channel or in a software thread. | ||
| 444 | */ | 482 | */ |
| 445 | u32 host1x_syncpt_read_max(struct host1x_syncpt *sp) | 483 | u32 host1x_syncpt_read_max(struct host1x_syncpt *sp) |
| 446 | { | 484 | { |
| @@ -450,8 +488,12 @@ u32 host1x_syncpt_read_max(struct host1x_syncpt *sp) | |||
| 450 | } | 488 | } |
| 451 | EXPORT_SYMBOL(host1x_syncpt_read_max); | 489 | EXPORT_SYMBOL(host1x_syncpt_read_max); |
| 452 | 490 | ||
| 453 | /* | 491 | /** |
| 454 | * Read min, which is a shadow of the current sync point value in hardware. | 492 | * host1x_syncpt_read_min() - read minimum syncpoint value |
| 493 | * @sp: host1x syncpoint | ||
| 494 | * | ||
| 495 | * The minimum syncpoint value is a shadow of the current sync point value in | ||
| 496 | * hardware. | ||
| 455 | */ | 497 | */ |
| 456 | u32 host1x_syncpt_read_min(struct host1x_syncpt *sp) | 498 | u32 host1x_syncpt_read_min(struct host1x_syncpt *sp) |
| 457 | { | 499 | { |
| @@ -461,6 +503,10 @@ u32 host1x_syncpt_read_min(struct host1x_syncpt *sp) | |||
| 461 | } | 503 | } |
| 462 | EXPORT_SYMBOL(host1x_syncpt_read_min); | 504 | EXPORT_SYMBOL(host1x_syncpt_read_min); |
| 463 | 505 | ||
| 506 | /** | ||
| 507 | * host1x_syncpt_read() - read the current syncpoint value | ||
| 508 | * @sp: host1x syncpoint | ||
| 509 | */ | ||
| 464 | u32 host1x_syncpt_read(struct host1x_syncpt *sp) | 510 | u32 host1x_syncpt_read(struct host1x_syncpt *sp) |
| 465 | { | 511 | { |
| 466 | return host1x_syncpt_load(sp); | 512 | return host1x_syncpt_load(sp); |
| @@ -482,6 +528,11 @@ unsigned int host1x_syncpt_nb_mlocks(struct host1x *host) | |||
| 482 | return host->info->nb_mlocks; | 528 | return host->info->nb_mlocks; |
| 483 | } | 529 | } |
| 484 | 530 | ||
| 531 | /** | ||
| 532 | * host1x_syncpt_get() - obtain a syncpoint by ID | ||
| 533 | * @host: host1x controller | ||
| 534 | * @id: syncpoint ID | ||
| 535 | */ | ||
| 485 | struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, unsigned int id) | 536 | struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, unsigned int id) |
| 486 | { | 537 | { |
| 487 | if (id >= host->info->nb_pts) | 538 | if (id >= host->info->nb_pts) |
| @@ -491,12 +542,20 @@ struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, unsigned int id) | |||
| 491 | } | 542 | } |
| 492 | EXPORT_SYMBOL(host1x_syncpt_get); | 543 | EXPORT_SYMBOL(host1x_syncpt_get); |
| 493 | 544 | ||
| 545 | /** | ||
| 546 | * host1x_syncpt_get_base() - obtain the wait base associated with a syncpoint | ||
| 547 | * @sp: host1x syncpoint | ||
| 548 | */ | ||
| 494 | struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp) | 549 | struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp) |
| 495 | { | 550 | { |
| 496 | return sp ? sp->base : NULL; | 551 | return sp ? sp->base : NULL; |
| 497 | } | 552 | } |
| 498 | EXPORT_SYMBOL(host1x_syncpt_get_base); | 553 | EXPORT_SYMBOL(host1x_syncpt_get_base); |
| 499 | 554 | ||
| 555 | /** | ||
| 556 | * host1x_syncpt_base_id() - retrieve the ID of a syncpoint wait base | ||
| 557 | * @base: host1x syncpoint wait base | ||
| 558 | */ | ||
| 500 | u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base) | 559 | u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base) |
| 501 | { | 560 | { |
| 502 | return base->id; | 561 | return base->id; |
diff --git a/include/linux/host1x.h b/include/linux/host1x.h index 3d04aa1dc83e..630b1a98ab58 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h | |||
| @@ -32,11 +32,27 @@ enum host1x_class { | |||
| 32 | 32 | ||
| 33 | struct host1x_client; | 33 | struct host1x_client; |
| 34 | 34 | ||
| 35 | /** | ||
| 36 | * struct host1x_client_ops - host1x client operations | ||
| 37 | * @init: host1x client initialization code | ||
| 38 | * @exit: host1x client tear down code | ||
| 39 | */ | ||
| 35 | struct host1x_client_ops { | 40 | struct host1x_client_ops { |
| 36 | int (*init)(struct host1x_client *client); | 41 | int (*init)(struct host1x_client *client); |
| 37 | int (*exit)(struct host1x_client *client); | 42 | int (*exit)(struct host1x_client *client); |
| 38 | }; | 43 | }; |
| 39 | 44 | ||
| 45 | /** | ||
| 46 | * struct host1x_client - host1x client structure | ||
| 47 | * @list: list node for the host1x client | ||
| 48 | * @parent: pointer to struct device representing the host1x controller | ||
| 49 | * @dev: pointer to struct device backing this host1x client | ||
| 50 | * @ops: host1x client operations | ||
| 51 | * @class: host1x class represented by this client | ||
| 52 | * @channel: host1x channel associated with this client | ||
| 53 | * @syncpts: array of syncpoints requested for this client | ||
| 54 | * @num_syncpts: number of syncpoints requested for this client | ||
| 55 | */ | ||
| 40 | struct host1x_client { | 56 | struct host1x_client { |
| 41 | struct list_head list; | 57 | struct list_head list; |
| 42 | struct device *parent; | 58 | struct device *parent; |
| @@ -156,7 +172,6 @@ struct host1x_channel; | |||
| 156 | struct host1x_job; | 172 | struct host1x_job; |
| 157 | 173 | ||
| 158 | struct host1x_channel *host1x_channel_request(struct device *dev); | 174 | struct host1x_channel *host1x_channel_request(struct device *dev); |
| 159 | void host1x_channel_free(struct host1x_channel *channel); | ||
| 160 | struct host1x_channel *host1x_channel_get(struct host1x_channel *channel); | 175 | struct host1x_channel *host1x_channel_get(struct host1x_channel *channel); |
| 161 | void host1x_channel_put(struct host1x_channel *channel); | 176 | void host1x_channel_put(struct host1x_channel *channel); |
| 162 | int host1x_job_submit(struct host1x_job *job); | 177 | int host1x_job_submit(struct host1x_job *job); |
| @@ -177,6 +192,13 @@ struct host1x_reloc { | |||
| 177 | unsigned long shift; | 192 | unsigned long shift; |
| 178 | }; | 193 | }; |
| 179 | 194 | ||
| 195 | struct host1x_waitchk { | ||
| 196 | struct host1x_bo *bo; | ||
| 197 | u32 offset; | ||
| 198 | u32 syncpt_id; | ||
| 199 | u32 thresh; | ||
| 200 | }; | ||
| 201 | |||
| 180 | struct host1x_job { | 202 | struct host1x_job { |
| 181 | /* When refcount goes to zero, job can be freed */ | 203 | /* When refcount goes to zero, job can be freed */ |
| 182 | struct kref ref; | 204 | struct kref ref; |
| @@ -226,7 +248,10 @@ struct host1x_job { | |||
| 226 | u8 *gather_copy_mapped; | 248 | u8 *gather_copy_mapped; |
| 227 | 249 | ||
| 228 | /* Check if register is marked as an address reg */ | 250 | /* Check if register is marked as an address reg */ |
| 229 | int (*is_addr_reg)(struct device *dev, u32 reg, u32 class); | 251 | int (*is_addr_reg)(struct device *dev, u32 class, u32 reg); |
| 252 | |||
| 253 | /* Check if class belongs to the unit */ | ||
| 254 | int (*is_valid_class)(u32 class); | ||
| 230 | 255 | ||
| 231 | /* Request a SETCLASS to this class */ | 256 | /* Request a SETCLASS to this class */ |
| 232 | u32 class; | 257 | u32 class; |
| @@ -251,6 +276,15 @@ void host1x_job_unpin(struct host1x_job *job); | |||
| 251 | 276 | ||
| 252 | struct host1x_device; | 277 | struct host1x_device; |
| 253 | 278 | ||
| 279 | /** | ||
| 280 | * struct host1x_driver - host1x logical device driver | ||
| 281 | * @driver: core driver | ||
| 282 | * @subdevs: table of OF device IDs matching subdevices for this driver | ||
| 283 | * @list: list node for the driver | ||
| 284 | * @probe: called when the host1x logical device is probed | ||
| 285 | * @remove: called when the host1x logical device is removed | ||
| 286 | * @shutdown: called when the host1x logical device is shut down | ||
| 287 | */ | ||
| 254 | struct host1x_driver { | 288 | struct host1x_driver { |
| 255 | struct device_driver driver; | 289 | struct device_driver driver; |
| 256 | 290 | ||
