diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/tegra/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.c | 57 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/hda.c | 63 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/hda.h | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/hdmi.c | 217 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/output.c | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/sor.c | 64 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/vic.c | 75 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/vic.h | 9 | ||||
-rw-r--r-- | drivers/gpu/host1x/bus.c | 35 | ||||
-rw-r--r-- | drivers/gpu/host1x/cdma.c | 189 | ||||
-rw-r--r-- | drivers/gpu/host1x/cdma.h | 8 | ||||
-rw-r--r-- | drivers/gpu/host1x/dev.c | 49 | ||||
-rw-r--r-- | drivers/gpu/host1x/dev.h | 8 | ||||
-rw-r--r-- | drivers/gpu/host1x/hw/cdma_hw.c | 46 | ||||
-rw-r--r-- | drivers/gpu/host1x/hw/channel_hw.c | 43 | ||||
-rw-r--r-- | drivers/gpu/host1x/hw/host1x06_hardware.h | 6 | ||||
-rw-r--r-- | drivers/gpu/host1x/hw/host1x07_hardware.h | 6 | ||||
-rw-r--r-- | drivers/gpu/host1x/hw/hw_host1x06_channel.h | 11 | ||||
-rw-r--r-- | drivers/gpu/host1x/hw/hw_host1x07_channel.h | 11 |
21 files changed, 621 insertions, 311 deletions
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile index 2e0d6213f6bc..33c463e8d49f 100644 --- a/drivers/gpu/drm/tegra/Makefile +++ b/drivers/gpu/drm/tegra/Makefile | |||
@@ -10,6 +10,7 @@ tegra-drm-y := \ | |||
10 | dc.o \ | 10 | dc.o \ |
11 | output.o \ | 11 | output.o \ |
12 | rgb.o \ | 12 | rgb.o \ |
13 | hda.o \ | ||
13 | hdmi.o \ | 14 | hdmi.o \ |
14 | mipi-phy.o \ | 15 | mipi-phy.o \ |
15 | dsi.o \ | 16 | dsi.o \ |
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 4b70ce664c41..0c5f1e6a0446 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c | |||
@@ -92,10 +92,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) | |||
92 | return -ENOMEM; | 92 | return -ENOMEM; |
93 | 93 | ||
94 | if (iommu_present(&platform_bus_type)) { | 94 | if (iommu_present(&platform_bus_type)) { |
95 | u64 carveout_start, carveout_end, gem_start, gem_end; | ||
96 | struct iommu_domain_geometry *geometry; | ||
97 | unsigned long order; | ||
98 | |||
99 | tegra->domain = iommu_domain_alloc(&platform_bus_type); | 95 | tegra->domain = iommu_domain_alloc(&platform_bus_type); |
100 | if (!tegra->domain) { | 96 | if (!tegra->domain) { |
101 | err = -ENOMEM; | 97 | err = -ENOMEM; |
@@ -105,27 +101,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) | |||
105 | err = iova_cache_get(); | 101 | err = iova_cache_get(); |
106 | if (err < 0) | 102 | if (err < 0) |
107 | goto domain; | 103 | goto domain; |
108 | |||
109 | geometry = &tegra->domain->geometry; | ||
110 | gem_start = geometry->aperture_start; | ||
111 | gem_end = geometry->aperture_end - CARVEOUT_SZ; | ||
112 | carveout_start = gem_end + 1; | ||
113 | carveout_end = geometry->aperture_end; | ||
114 | |||
115 | order = __ffs(tegra->domain->pgsize_bitmap); | ||
116 | init_iova_domain(&tegra->carveout.domain, 1UL << order, | ||
117 | carveout_start >> order); | ||
118 | |||
119 | tegra->carveout.shift = iova_shift(&tegra->carveout.domain); | ||
120 | tegra->carveout.limit = carveout_end >> tegra->carveout.shift; | ||
121 | |||
122 | drm_mm_init(&tegra->mm, gem_start, gem_end - gem_start + 1); | ||
123 | mutex_init(&tegra->mm_lock); | ||
124 | |||
125 | DRM_DEBUG("IOMMU apertures:\n"); | ||
126 | DRM_DEBUG(" GEM: %#llx-%#llx\n", gem_start, gem_end); | ||
127 | DRM_DEBUG(" Carveout: %#llx-%#llx\n", carveout_start, | ||
128 | carveout_end); | ||
129 | } | 104 | } |
130 | 105 | ||
131 | mutex_init(&tegra->clients_lock); | 106 | mutex_init(&tegra->clients_lock); |
@@ -159,6 +134,36 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) | |||
159 | if (err < 0) | 134 | if (err < 0) |
160 | goto fbdev; | 135 | goto fbdev; |
161 | 136 | ||
137 | if (tegra->domain) { | ||
138 | u64 carveout_start, carveout_end, gem_start, gem_end; | ||
139 | u64 dma_mask = dma_get_mask(&device->dev); | ||
140 | dma_addr_t start, end; | ||
141 | unsigned long order; | ||
142 | |||
143 | start = tegra->domain->geometry.aperture_start & dma_mask; | ||
144 | end = tegra->domain->geometry.aperture_end & dma_mask; | ||
145 | |||
146 | gem_start = start; | ||
147 | gem_end = end - CARVEOUT_SZ; | ||
148 | carveout_start = gem_end + 1; | ||
149 | carveout_end = end; | ||
150 | |||
151 | order = __ffs(tegra->domain->pgsize_bitmap); | ||
152 | init_iova_domain(&tegra->carveout.domain, 1UL << order, | ||
153 | carveout_start >> order); | ||
154 | |||
155 | tegra->carveout.shift = iova_shift(&tegra->carveout.domain); | ||
156 | tegra->carveout.limit = carveout_end >> tegra->carveout.shift; | ||
157 | |||
158 | drm_mm_init(&tegra->mm, gem_start, gem_end - gem_start + 1); | ||
159 | mutex_init(&tegra->mm_lock); | ||
160 | |||
161 | DRM_DEBUG("IOMMU apertures:\n"); | ||
162 | DRM_DEBUG(" GEM: %#llx-%#llx\n", gem_start, gem_end); | ||
163 | DRM_DEBUG(" Carveout: %#llx-%#llx\n", carveout_start, | ||
164 | carveout_end); | ||
165 | } | ||
166 | |||
162 | if (tegra->hub) { | 167 | if (tegra->hub) { |
163 | err = tegra_display_hub_prepare(tegra->hub); | 168 | err = tegra_display_hub_prepare(tegra->hub); |
164 | if (err < 0) | 169 | if (err < 0) |
@@ -1041,6 +1046,7 @@ int tegra_drm_register_client(struct tegra_drm *tegra, | |||
1041 | { | 1046 | { |
1042 | mutex_lock(&tegra->clients_lock); | 1047 | mutex_lock(&tegra->clients_lock); |
1043 | list_add_tail(&client->list, &tegra->clients); | 1048 | list_add_tail(&client->list, &tegra->clients); |
1049 | client->drm = tegra; | ||
1044 | mutex_unlock(&tegra->clients_lock); | 1050 | mutex_unlock(&tegra->clients_lock); |
1045 | 1051 | ||
1046 | return 0; | 1052 | return 0; |
@@ -1051,6 +1057,7 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, | |||
1051 | { | 1057 | { |
1052 | mutex_lock(&tegra->clients_lock); | 1058 | mutex_lock(&tegra->clients_lock); |
1053 | list_del_init(&client->list); | 1059 | list_del_init(&client->list); |
1060 | client->drm = NULL; | ||
1054 | mutex_unlock(&tegra->clients_lock); | 1061 | mutex_unlock(&tegra->clients_lock); |
1055 | 1062 | ||
1056 | return 0; | 1063 | return 0; |
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index e2dee5c9c03b..70154c253d45 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h | |||
@@ -88,6 +88,7 @@ int tegra_drm_submit(struct tegra_drm_context *context, | |||
88 | struct tegra_drm_client { | 88 | struct tegra_drm_client { |
89 | struct host1x_client base; | 89 | struct host1x_client base; |
90 | struct list_head list; | 90 | struct list_head list; |
91 | struct tegra_drm *drm; | ||
91 | 92 | ||
92 | unsigned int version; | 93 | unsigned int version; |
93 | const struct tegra_drm_client_ops *ops; | 94 | const struct tegra_drm_client_ops *ops; |
@@ -124,7 +125,7 @@ struct tegra_output { | |||
124 | struct drm_panel *panel; | 125 | struct drm_panel *panel; |
125 | struct i2c_adapter *ddc; | 126 | struct i2c_adapter *ddc; |
126 | const struct edid *edid; | 127 | const struct edid *edid; |
127 | struct cec_notifier *notifier; | 128 | struct cec_notifier *cec; |
128 | unsigned int hpd_irq; | 129 | unsigned int hpd_irq; |
129 | int hpd_gpio; | 130 | int hpd_gpio; |
130 | enum of_gpio_flags hpd_gpio_flags; | 131 | enum of_gpio_flags hpd_gpio_flags; |
diff --git a/drivers/gpu/drm/tegra/hda.c b/drivers/gpu/drm/tegra/hda.c new file mode 100644 index 000000000000..94245a18a043 --- /dev/null +++ b/drivers/gpu/drm/tegra/hda.c | |||
@@ -0,0 +1,63 @@ | |||
1 | // SPDX-License-Identifier: MIT | ||
2 | /* | ||
3 | * Copyright (C) 2019 NVIDIA Corporation | ||
4 | */ | ||
5 | |||
6 | #include <linux/bug.h> | ||
7 | |||
8 | #include <sound/hda_verbs.h> | ||
9 | |||
10 | #include "hda.h" | ||
11 | |||
12 | void tegra_hda_parse_format(unsigned int format, struct tegra_hda_format *fmt) | ||
13 | { | ||
14 | unsigned int mul, div, bits, channels; | ||
15 | |||
16 | if (format & AC_FMT_TYPE_NON_PCM) | ||
17 | fmt->pcm = false; | ||
18 | else | ||
19 | fmt->pcm = true; | ||
20 | |||
21 | if (format & AC_FMT_BASE_44K) | ||
22 | fmt->sample_rate = 44100; | ||
23 | else | ||
24 | fmt->sample_rate = 48000; | ||
25 | |||
26 | mul = (format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT; | ||
27 | div = (format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT; | ||
28 | |||
29 | fmt->sample_rate *= (mul + 1) / (div + 1); | ||
30 | |||
31 | switch (format & AC_FMT_BITS_MASK) { | ||
32 | case AC_FMT_BITS_8: | ||
33 | fmt->bits = 8; | ||
34 | break; | ||
35 | |||
36 | case AC_FMT_BITS_16: | ||
37 | fmt->bits = 16; | ||
38 | break; | ||
39 | |||
40 | case AC_FMT_BITS_20: | ||
41 | fmt->bits = 20; | ||
42 | break; | ||
43 | |||
44 | case AC_FMT_BITS_24: | ||
45 | fmt->bits = 24; | ||
46 | break; | ||
47 | |||
48 | case AC_FMT_BITS_32: | ||
49 | fmt->bits = 32; | ||
50 | break; | ||
51 | |||
52 | default: | ||
53 | bits = (format & AC_FMT_BITS_MASK) >> AC_FMT_BITS_SHIFT; | ||
54 | WARN(1, "invalid number of bits: %#x\n", bits); | ||
55 | fmt->bits = 8; | ||
56 | break; | ||
57 | } | ||
58 | |||
59 | channels = (format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT; | ||
60 | |||
61 | /* channels are encoded as n - 1 */ | ||
62 | fmt->channels = channels + 1; | ||
63 | } | ||
diff --git a/drivers/gpu/drm/tegra/hda.h b/drivers/gpu/drm/tegra/hda.h new file mode 100644 index 000000000000..77269955a4f2 --- /dev/null +++ b/drivers/gpu/drm/tegra/hda.h | |||
@@ -0,0 +1,20 @@ | |||
1 | // SPDX-License-Identifier: MIT | ||
2 | /* | ||
3 | * Copyright (C) 2019 NVIDIA Corporation | ||
4 | */ | ||
5 | |||
6 | #ifndef DRM_TEGRA_HDA_H | ||
7 | #define DRM_TEGRA_HDA_H 1 | ||
8 | |||
9 | #include <linux/types.h> | ||
10 | |||
11 | struct tegra_hda_format { | ||
12 | unsigned int sample_rate; | ||
13 | unsigned int channels; | ||
14 | unsigned int bits; | ||
15 | bool pcm; | ||
16 | }; | ||
17 | |||
18 | void tegra_hda_parse_format(unsigned int format, struct tegra_hda_format *fmt); | ||
19 | |||
20 | #endif | ||
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 78bc7c906e22..47c55974756d 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/debugfs.h> | 11 | #include <linux/debugfs.h> |
12 | #include <linux/gpio.h> | 12 | #include <linux/gpio.h> |
13 | #include <linux/hdmi.h> | 13 | #include <linux/hdmi.h> |
14 | #include <linux/math64.h> | ||
14 | #include <linux/of_device.h> | 15 | #include <linux/of_device.h> |
15 | #include <linux/pm_runtime.h> | 16 | #include <linux/pm_runtime.h> |
16 | #include <linux/regulator/consumer.h> | 17 | #include <linux/regulator/consumer.h> |
@@ -20,10 +21,7 @@ | |||
20 | #include <drm/drm_crtc.h> | 21 | #include <drm/drm_crtc.h> |
21 | #include <drm/drm_probe_helper.h> | 22 | #include <drm/drm_probe_helper.h> |
22 | 23 | ||
23 | #include <sound/hda_verbs.h> | 24 | #include "hda.h" |
24 | |||
25 | #include <media/cec-notifier.h> | ||
26 | |||
27 | #include "hdmi.h" | 25 | #include "hdmi.h" |
28 | #include "drm.h" | 26 | #include "drm.h" |
29 | #include "dc.h" | 27 | #include "dc.h" |
@@ -71,8 +69,7 @@ struct tegra_hdmi { | |||
71 | const struct tegra_hdmi_config *config; | 69 | const struct tegra_hdmi_config *config; |
72 | 70 | ||
73 | unsigned int audio_source; | 71 | unsigned int audio_source; |
74 | unsigned int audio_sample_rate; | 72 | struct tegra_hda_format format; |
75 | unsigned int audio_channels; | ||
76 | 73 | ||
77 | unsigned int pixel_clock; | 74 | unsigned int pixel_clock; |
78 | bool stereo; | 75 | bool stereo; |
@@ -119,68 +116,11 @@ static inline void tegra_hdmi_writel(struct tegra_hdmi *hdmi, u32 value, | |||
119 | } | 116 | } |
120 | 117 | ||
121 | struct tegra_hdmi_audio_config { | 118 | struct tegra_hdmi_audio_config { |
122 | unsigned int pclk; | ||
123 | unsigned int n; | 119 | unsigned int n; |
124 | unsigned int cts; | 120 | unsigned int cts; |
125 | unsigned int aval; | 121 | unsigned int aval; |
126 | }; | 122 | }; |
127 | 123 | ||
128 | static const struct tegra_hdmi_audio_config tegra_hdmi_audio_32k[] = { | ||
129 | { 25200000, 4096, 25200, 24000 }, | ||
130 | { 27000000, 4096, 27000, 24000 }, | ||
131 | { 74250000, 4096, 74250, 24000 }, | ||
132 | { 148500000, 4096, 148500, 24000 }, | ||
133 | { 0, 0, 0, 0 }, | ||
134 | }; | ||
135 | |||
136 | static const struct tegra_hdmi_audio_config tegra_hdmi_audio_44_1k[] = { | ||
137 | { 25200000, 5880, 26250, 25000 }, | ||
138 | { 27000000, 5880, 28125, 25000 }, | ||
139 | { 74250000, 4704, 61875, 20000 }, | ||
140 | { 148500000, 4704, 123750, 20000 }, | ||
141 | { 0, 0, 0, 0 }, | ||
142 | }; | ||
143 | |||
144 | static const struct tegra_hdmi_audio_config tegra_hdmi_audio_48k[] = { | ||
145 | { 25200000, 6144, 25200, 24000 }, | ||
146 | { 27000000, 6144, 27000, 24000 }, | ||
147 | { 74250000, 6144, 74250, 24000 }, | ||
148 | { 148500000, 6144, 148500, 24000 }, | ||
149 | { 0, 0, 0, 0 }, | ||
150 | }; | ||
151 | |||
152 | static const struct tegra_hdmi_audio_config tegra_hdmi_audio_88_2k[] = { | ||
153 | { 25200000, 11760, 26250, 25000 }, | ||
154 | { 27000000, 11760, 28125, 25000 }, | ||
155 | { 74250000, 9408, 61875, 20000 }, | ||
156 | { 148500000, 9408, 123750, 20000 }, | ||
157 | { 0, 0, 0, 0 }, | ||
158 | }; | ||
159 | |||
160 | static const struct tegra_hdmi_audio_config tegra_hdmi_audio_96k[] = { | ||
161 | { 25200000, 12288, 25200, 24000 }, | ||
162 | { 27000000, 12288, 27000, 24000 }, | ||
163 | { 74250000, 12288, 74250, 24000 }, | ||
164 | { 148500000, 12288, 148500, 24000 }, | ||
165 | { 0, 0, 0, 0 }, | ||
166 | }; | ||
167 | |||
168 | static const struct tegra_hdmi_audio_config tegra_hdmi_audio_176_4k[] = { | ||
169 | { 25200000, 23520, 26250, 25000 }, | ||
170 | { 27000000, 23520, 28125, 25000 }, | ||
171 | { 74250000, 18816, 61875, 20000 }, | ||
172 | { 148500000, 18816, 123750, 20000 }, | ||
173 | { 0, 0, 0, 0 }, | ||
174 | }; | ||
175 | |||
176 | static const struct tegra_hdmi_audio_config tegra_hdmi_audio_192k[] = { | ||
177 | { 25200000, 24576, 25200, 24000 }, | ||
178 | { 27000000, 24576, 27000, 24000 }, | ||
179 | { 74250000, 24576, 74250, 24000 }, | ||
180 | { 148500000, 24576, 148500, 24000 }, | ||
181 | { 0, 0, 0, 0 }, | ||
182 | }; | ||
183 | |||
184 | static const struct tmds_config tegra20_tmds_config[] = { | 124 | static const struct tmds_config tegra20_tmds_config[] = { |
185 | { /* slow pixel clock modes */ | 125 | { /* slow pixel clock modes */ |
186 | .pclk = 27000000, | 126 | .pclk = 27000000, |
@@ -418,52 +358,53 @@ static const struct tmds_config tegra124_tmds_config[] = { | |||
418 | }, | 358 | }, |
419 | }; | 359 | }; |
420 | 360 | ||
421 | static const struct tegra_hdmi_audio_config * | 361 | static int |
422 | tegra_hdmi_get_audio_config(unsigned int sample_rate, unsigned int pclk) | 362 | tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pix_clock, |
363 | struct tegra_hdmi_audio_config *config) | ||
423 | { | 364 | { |
424 | const struct tegra_hdmi_audio_config *table; | 365 | const unsigned int afreq = 128 * audio_freq; |
425 | 366 | const unsigned int min_n = afreq / 1500; | |
426 | switch (sample_rate) { | 367 | const unsigned int max_n = afreq / 300; |
427 | case 32000: | 368 | const unsigned int ideal_n = afreq / 1000; |
428 | table = tegra_hdmi_audio_32k; | 369 | int64_t min_err = (uint64_t)-1 >> 1; |
429 | break; | 370 | unsigned int min_delta = -1; |
430 | 371 | int n; | |
431 | case 44100: | 372 | |
432 | table = tegra_hdmi_audio_44_1k; | 373 | memset(config, 0, sizeof(*config)); |
433 | break; | 374 | config->n = -1; |
434 | 375 | ||
435 | case 48000: | 376 | for (n = min_n; n <= max_n; n++) { |
436 | table = tegra_hdmi_audio_48k; | 377 | uint64_t cts_f, aval_f; |
437 | break; | 378 | unsigned int delta; |
438 | 379 | int64_t cts, err; | |
439 | case 88200: | 380 | |
440 | table = tegra_hdmi_audio_88_2k; | 381 | /* compute aval in 48.16 fixed point */ |
441 | break; | 382 | aval_f = ((int64_t)24000000 << 16) * n; |
442 | 383 | do_div(aval_f, afreq); | |
443 | case 96000: | 384 | /* It should round without any rest */ |
444 | table = tegra_hdmi_audio_96k; | 385 | if (aval_f & 0xFFFF) |
445 | break; | 386 | continue; |
446 | 387 | ||
447 | case 176400: | 388 | /* Compute cts in 48.16 fixed point */ |
448 | table = tegra_hdmi_audio_176_4k; | 389 | cts_f = ((int64_t)pix_clock << 16) * n; |
449 | break; | 390 | do_div(cts_f, afreq); |
450 | 391 | /* Round it to the nearest integer */ | |
451 | case 192000: | 392 | cts = (cts_f & ~0xFFFF) + ((cts_f & BIT(15)) << 1); |
452 | table = tegra_hdmi_audio_192k; | 393 | |
453 | break; | 394 | delta = abs(n - ideal_n); |
454 | 395 | ||
455 | default: | 396 | /* Compute the absolute error */ |
456 | return NULL; | 397 | err = abs((int64_t)cts_f - cts); |
457 | } | 398 | if (err < min_err || (err == min_err && delta < min_delta)) { |
458 | 399 | config->n = n; | |
459 | while (table->pclk) { | 400 | config->cts = cts >> 16; |
460 | if (table->pclk == pclk) | 401 | config->aval = aval_f >> 16; |
461 | return table; | 402 | min_delta = delta; |
462 | 403 | min_err = err; | |
463 | table++; | 404 | } |
464 | } | 405 | } |
465 | 406 | ||
466 | return NULL; | 407 | return config->n != -1 ? 0 : -EINVAL; |
467 | } | 408 | } |
468 | 409 | ||
469 | static void tegra_hdmi_setup_audio_fs_tables(struct tegra_hdmi *hdmi) | 410 | static void tegra_hdmi_setup_audio_fs_tables(struct tegra_hdmi *hdmi) |
@@ -510,7 +451,7 @@ static void tegra_hdmi_write_aval(struct tegra_hdmi *hdmi, u32 value) | |||
510 | unsigned int i; | 451 | unsigned int i; |
511 | 452 | ||
512 | for (i = 0; i < ARRAY_SIZE(regs); i++) { | 453 | for (i = 0; i < ARRAY_SIZE(regs); i++) { |
513 | if (regs[i].sample_rate == hdmi->audio_sample_rate) { | 454 | if (regs[i].sample_rate == hdmi->format.sample_rate) { |
514 | tegra_hdmi_writel(hdmi, value, regs[i].offset); | 455 | tegra_hdmi_writel(hdmi, value, regs[i].offset); |
515 | break; | 456 | break; |
516 | } | 457 | } |
@@ -519,8 +460,9 @@ static void tegra_hdmi_write_aval(struct tegra_hdmi *hdmi, u32 value) | |||
519 | 460 | ||
520 | static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi) | 461 | static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi) |
521 | { | 462 | { |
522 | const struct tegra_hdmi_audio_config *config; | 463 | struct tegra_hdmi_audio_config config; |
523 | u32 source, value; | 464 | u32 source, value; |
465 | int err; | ||
524 | 466 | ||
525 | switch (hdmi->audio_source) { | 467 | switch (hdmi->audio_source) { |
526 | case HDA: | 468 | case HDA: |
@@ -564,7 +506,7 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi) | |||
564 | * play back system startup sounds early. It is possibly not | 506 | * play back system startup sounds early. It is possibly not |
565 | * needed on Linux at all. | 507 | * needed on Linux at all. |
566 | */ | 508 | */ |
567 | if (hdmi->audio_channels == 2) | 509 | if (hdmi->format.channels == 2) |
568 | value = SOR_AUDIO_CNTRL0_INJECT_NULLSMPL; | 510 | value = SOR_AUDIO_CNTRL0_INJECT_NULLSMPL; |
569 | else | 511 | else |
570 | value = 0; | 512 | value = 0; |
@@ -595,25 +537,28 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi) | |||
595 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_SPARE0); | 537 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_SPARE0); |
596 | } | 538 | } |
597 | 539 | ||
598 | config = tegra_hdmi_get_audio_config(hdmi->audio_sample_rate, | 540 | err = tegra_hdmi_get_audio_config(hdmi->format.sample_rate, |
599 | hdmi->pixel_clock); | 541 | hdmi->pixel_clock, &config); |
600 | if (!config) { | 542 | if (err < 0) { |
601 | dev_err(hdmi->dev, | 543 | dev_err(hdmi->dev, |
602 | "cannot set audio to %u Hz at %u Hz pixel clock\n", | 544 | "cannot set audio to %u Hz at %u Hz pixel clock\n", |
603 | hdmi->audio_sample_rate, hdmi->pixel_clock); | 545 | hdmi->format.sample_rate, hdmi->pixel_clock); |
604 | return -EINVAL; | 546 | return err; |
605 | } | 547 | } |
606 | 548 | ||
549 | dev_dbg(hdmi->dev, "audio: pixclk=%u, n=%u, cts=%u, aval=%u\n", | ||
550 | hdmi->pixel_clock, config.n, config.cts, config.aval); | ||
551 | |||
607 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_HDMI_ACR_CTRL); | 552 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_HDMI_ACR_CTRL); |
608 | 553 | ||
609 | value = AUDIO_N_RESETF | AUDIO_N_GENERATE_ALTERNATE | | 554 | value = AUDIO_N_RESETF | AUDIO_N_GENERATE_ALTERNATE | |
610 | AUDIO_N_VALUE(config->n - 1); | 555 | AUDIO_N_VALUE(config.n - 1); |
611 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N); | 556 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N); |
612 | 557 | ||
613 | tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config->n) | ACR_ENABLE, | 558 | tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config.n) | ACR_ENABLE, |
614 | HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH); | 559 | HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH); |
615 | 560 | ||
616 | tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config->cts), | 561 | tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config.cts), |
617 | HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW); | 562 | HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW); |
618 | 563 | ||
619 | value = SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1); | 564 | value = SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1); |
@@ -624,7 +569,7 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi) | |||
624 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N); | 569 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N); |
625 | 570 | ||
626 | if (hdmi->config->has_hda) | 571 | if (hdmi->config->has_hda) |
627 | tegra_hdmi_write_aval(hdmi, config->aval); | 572 | tegra_hdmi_write_aval(hdmi, config.aval); |
628 | 573 | ||
629 | tegra_hdmi_setup_audio_fs_tables(hdmi); | 574 | tegra_hdmi_setup_audio_fs_tables(hdmi); |
630 | 575 | ||
@@ -788,7 +733,7 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) | |||
788 | return; | 733 | return; |
789 | } | 734 | } |
790 | 735 | ||
791 | frame.channels = hdmi->audio_channels; | 736 | frame.channels = hdmi->format.channels; |
792 | 737 | ||
793 | err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); | 738 | err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); |
794 | if (err < 0) { | 739 | if (err < 0) { |
@@ -1590,24 +1535,6 @@ static const struct of_device_id tegra_hdmi_of_match[] = { | |||
1590 | }; | 1535 | }; |
1591 | MODULE_DEVICE_TABLE(of, tegra_hdmi_of_match); | 1536 | MODULE_DEVICE_TABLE(of, tegra_hdmi_of_match); |
1592 | 1537 | ||
1593 | static void hda_format_parse(unsigned int format, unsigned int *rate, | ||
1594 | unsigned int *channels) | ||
1595 | { | ||
1596 | unsigned int mul, div; | ||
1597 | |||
1598 | if (format & AC_FMT_BASE_44K) | ||
1599 | *rate = 44100; | ||
1600 | else | ||
1601 | *rate = 48000; | ||
1602 | |||
1603 | mul = (format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT; | ||
1604 | div = (format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT; | ||
1605 | |||
1606 | *rate = *rate * (mul + 1) / (div + 1); | ||
1607 | |||
1608 | *channels = (format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT; | ||
1609 | } | ||
1610 | |||
1611 | static irqreturn_t tegra_hdmi_irq(int irq, void *data) | 1538 | static irqreturn_t tegra_hdmi_irq(int irq, void *data) |
1612 | { | 1539 | { |
1613 | struct tegra_hdmi *hdmi = data; | 1540 | struct tegra_hdmi *hdmi = data; |
@@ -1624,14 +1551,9 @@ static irqreturn_t tegra_hdmi_irq(int irq, void *data) | |||
1624 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0); | 1551 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0); |
1625 | 1552 | ||
1626 | if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) { | 1553 | if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) { |
1627 | unsigned int sample_rate, channels; | ||
1628 | |||
1629 | format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK; | 1554 | format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK; |
1630 | 1555 | ||
1631 | hda_format_parse(format, &sample_rate, &channels); | 1556 | tegra_hda_parse_format(format, &hdmi->format); |
1632 | |||
1633 | hdmi->audio_sample_rate = sample_rate; | ||
1634 | hdmi->audio_channels = channels; | ||
1635 | 1557 | ||
1636 | err = tegra_hdmi_setup_audio(hdmi); | 1558 | err = tegra_hdmi_setup_audio(hdmi); |
1637 | if (err < 0) { | 1559 | if (err < 0) { |
@@ -1665,8 +1587,6 @@ static int tegra_hdmi_probe(struct platform_device *pdev) | |||
1665 | hdmi->dev = &pdev->dev; | 1587 | hdmi->dev = &pdev->dev; |
1666 | 1588 | ||
1667 | hdmi->audio_source = AUTO; | 1589 | hdmi->audio_source = AUTO; |
1668 | hdmi->audio_sample_rate = 48000; | ||
1669 | hdmi->audio_channels = 2; | ||
1670 | hdmi->stereo = false; | 1590 | hdmi->stereo = false; |
1671 | hdmi->dvi = false; | 1591 | hdmi->dvi = false; |
1672 | 1592 | ||
@@ -1710,10 +1630,6 @@ static int tegra_hdmi_probe(struct platform_device *pdev) | |||
1710 | return PTR_ERR(hdmi->vdd); | 1630 | return PTR_ERR(hdmi->vdd); |
1711 | } | 1631 | } |
1712 | 1632 | ||
1713 | hdmi->output.notifier = cec_notifier_get(&pdev->dev); | ||
1714 | if (hdmi->output.notifier == NULL) | ||
1715 | return -ENOMEM; | ||
1716 | |||
1717 | hdmi->output.dev = &pdev->dev; | 1633 | hdmi->output.dev = &pdev->dev; |
1718 | 1634 | ||
1719 | err = tegra_output_probe(&hdmi->output); | 1635 | err = tegra_output_probe(&hdmi->output); |
@@ -1772,9 +1688,6 @@ static int tegra_hdmi_remove(struct platform_device *pdev) | |||
1772 | 1688 | ||
1773 | tegra_output_remove(&hdmi->output); | 1689 | tegra_output_remove(&hdmi->output); |
1774 | 1690 | ||
1775 | if (hdmi->output.notifier) | ||
1776 | cec_notifier_put(hdmi->output.notifier); | ||
1777 | |||
1778 | return 0; | 1691 | return 0; |
1779 | } | 1692 | } |
1780 | 1693 | ||
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index c662efc7e413..9c2b9dad55c3 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c | |||
@@ -36,7 +36,7 @@ int tegra_output_connector_get_modes(struct drm_connector *connector) | |||
36 | else if (output->ddc) | 36 | else if (output->ddc) |
37 | edid = drm_get_edid(connector, output->ddc); | 37 | edid = drm_get_edid(connector, output->ddc); |
38 | 38 | ||
39 | cec_notifier_set_phys_addr_from_edid(output->notifier, edid); | 39 | cec_notifier_set_phys_addr_from_edid(output->cec, edid); |
40 | drm_connector_update_edid_property(connector, edid); | 40 | drm_connector_update_edid_property(connector, edid); |
41 | 41 | ||
42 | if (edid) { | 42 | if (edid) { |
@@ -73,7 +73,7 @@ tegra_output_connector_detect(struct drm_connector *connector, bool force) | |||
73 | } | 73 | } |
74 | 74 | ||
75 | if (status != connector_status_connected) | 75 | if (status != connector_status_connected) |
76 | cec_notifier_phys_addr_invalidate(output->notifier); | 76 | cec_notifier_phys_addr_invalidate(output->cec); |
77 | 77 | ||
78 | return status; | 78 | return status; |
79 | } | 79 | } |
@@ -174,11 +174,18 @@ int tegra_output_probe(struct tegra_output *output) | |||
174 | disable_irq(output->hpd_irq); | 174 | disable_irq(output->hpd_irq); |
175 | } | 175 | } |
176 | 176 | ||
177 | output->cec = cec_notifier_get(output->dev); | ||
178 | if (!output->cec) | ||
179 | return -ENOMEM; | ||
180 | |||
177 | return 0; | 181 | return 0; |
178 | } | 182 | } |
179 | 183 | ||
180 | void tegra_output_remove(struct tegra_output *output) | 184 | void tegra_output_remove(struct tegra_output *output) |
181 | { | 185 | { |
186 | if (output->cec) | ||
187 | cec_notifier_put(output->cec); | ||
188 | |||
182 | if (gpio_is_valid(output->hpd_gpio)) { | 189 | if (gpio_is_valid(output->hpd_gpio)) { |
183 | free_irq(output->hpd_irq, output); | 190 | free_irq(output->hpd_irq, output); |
184 | gpio_free(output->hpd_gpio); | 191 | gpio_free(output->hpd_gpio); |
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 44feac2a0359..40057106f5f3 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c | |||
@@ -19,8 +19,6 @@ | |||
19 | 19 | ||
20 | #include <soc/tegra/pmc.h> | 20 | #include <soc/tegra/pmc.h> |
21 | 21 | ||
22 | #include <sound/hda_verbs.h> | ||
23 | |||
24 | #include <drm/drm_atomic_helper.h> | 22 | #include <drm/drm_atomic_helper.h> |
25 | #include <drm/drm_dp_helper.h> | 23 | #include <drm/drm_dp_helper.h> |
26 | #include <drm/drm_panel.h> | 24 | #include <drm/drm_panel.h> |
@@ -28,6 +26,7 @@ | |||
28 | 26 | ||
29 | #include "dc.h" | 27 | #include "dc.h" |
30 | #include "drm.h" | 28 | #include "drm.h" |
29 | #include "hda.h" | ||
31 | #include "sor.h" | 30 | #include "sor.h" |
32 | #include "trace.h" | 31 | #include "trace.h" |
33 | 32 | ||
@@ -411,6 +410,8 @@ struct tegra_sor { | |||
411 | struct clk *clk_dp; | 410 | struct clk *clk_dp; |
412 | struct clk *clk; | 411 | struct clk *clk; |
413 | 412 | ||
413 | u8 xbar_cfg[5]; | ||
414 | |||
414 | struct drm_dp_aux *aux; | 415 | struct drm_dp_aux *aux; |
415 | 416 | ||
416 | struct drm_info_list *debugfs_files; | 417 | struct drm_info_list *debugfs_files; |
@@ -429,10 +430,7 @@ struct tegra_sor { | |||
429 | struct delayed_work scdc; | 430 | struct delayed_work scdc; |
430 | bool scdc_enabled; | 431 | bool scdc_enabled; |
431 | 432 | ||
432 | struct { | 433 | struct tegra_hda_format format; |
433 | unsigned int sample_rate; | ||
434 | unsigned int channels; | ||
435 | } audio; | ||
436 | }; | 434 | }; |
437 | 435 | ||
438 | struct tegra_sor_state { | 436 | struct tegra_sor_state { |
@@ -1818,7 +1816,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) | |||
1818 | 1816 | ||
1819 | /* XXX not in TRM */ | 1817 | /* XXX not in TRM */ |
1820 | for (value = 0, i = 0; i < 5; i++) | 1818 | for (value = 0, i = 0; i < 5; i++) |
1821 | value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->soc->xbar_cfg[i]) | | 1819 | value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->xbar_cfg[i]) | |
1822 | SOR_XBAR_CTRL_LINK1_XSEL(i, i); | 1820 | SOR_XBAR_CTRL_LINK1_XSEL(i, i); |
1823 | 1821 | ||
1824 | tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL); | 1822 | tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL); |
@@ -2186,7 +2184,7 @@ static int tegra_sor_hdmi_enable_audio_infoframe(struct tegra_sor *sor) | |||
2186 | return err; | 2184 | return err; |
2187 | } | 2185 | } |
2188 | 2186 | ||
2189 | frame.channels = sor->audio.channels; | 2187 | frame.channels = sor->format.channels; |
2190 | 2188 | ||
2191 | err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); | 2189 | err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); |
2192 | if (err < 0) { | 2190 | if (err < 0) { |
@@ -2215,7 +2213,7 @@ static void tegra_sor_hdmi_audio_enable(struct tegra_sor *sor) | |||
2215 | value |= SOR_AUDIO_CNTRL_SOURCE_SELECT(SOURCE_SELECT_HDA); | 2213 | value |= SOR_AUDIO_CNTRL_SOURCE_SELECT(SOURCE_SELECT_HDA); |
2216 | 2214 | ||
2217 | /* inject null samples */ | 2215 | /* inject null samples */ |
2218 | if (sor->audio.channels != 2) | 2216 | if (sor->format.channels != 2) |
2219 | value &= ~SOR_AUDIO_CNTRL_INJECT_NULLSMPL; | 2217 | value &= ~SOR_AUDIO_CNTRL_INJECT_NULLSMPL; |
2220 | else | 2218 | else |
2221 | value |= SOR_AUDIO_CNTRL_INJECT_NULLSMPL; | 2219 | value |= SOR_AUDIO_CNTRL_INJECT_NULLSMPL; |
@@ -2246,7 +2244,7 @@ static void tegra_sor_hdmi_audio_enable(struct tegra_sor *sor) | |||
2246 | value = SOR_HDMI_AUDIO_N_RESET | SOR_HDMI_AUDIO_N_LOOKUP; | 2244 | value = SOR_HDMI_AUDIO_N_RESET | SOR_HDMI_AUDIO_N_LOOKUP; |
2247 | tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_N); | 2245 | tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_N); |
2248 | 2246 | ||
2249 | value = (24000 * 4096) / (128 * sor->audio.sample_rate / 1000); | 2247 | value = (24000 * 4096) / (128 * sor->format.sample_rate / 1000); |
2250 | tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0320); | 2248 | tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0320); |
2251 | tegra_sor_writel(sor, 4096, SOR_AUDIO_NVAL_0320); | 2249 | tegra_sor_writel(sor, 4096, SOR_AUDIO_NVAL_0320); |
2252 | 2250 | ||
@@ -2259,15 +2257,15 @@ static void tegra_sor_hdmi_audio_enable(struct tegra_sor *sor) | |||
2259 | tegra_sor_writel(sor, 20000, SOR_AUDIO_AVAL_1764); | 2257 | tegra_sor_writel(sor, 20000, SOR_AUDIO_AVAL_1764); |
2260 | tegra_sor_writel(sor, 18816, SOR_AUDIO_NVAL_1764); | 2258 | tegra_sor_writel(sor, 18816, SOR_AUDIO_NVAL_1764); |
2261 | 2259 | ||
2262 | value = (24000 * 6144) / (128 * sor->audio.sample_rate / 1000); | 2260 | value = (24000 * 6144) / (128 * sor->format.sample_rate / 1000); |
2263 | tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0480); | 2261 | tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0480); |
2264 | tegra_sor_writel(sor, 6144, SOR_AUDIO_NVAL_0480); | 2262 | tegra_sor_writel(sor, 6144, SOR_AUDIO_NVAL_0480); |
2265 | 2263 | ||
2266 | value = (24000 * 12288) / (128 * sor->audio.sample_rate / 1000); | 2264 | value = (24000 * 12288) / (128 * sor->format.sample_rate / 1000); |
2267 | tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0960); | 2265 | tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0960); |
2268 | tegra_sor_writel(sor, 12288, SOR_AUDIO_NVAL_0960); | 2266 | tegra_sor_writel(sor, 12288, SOR_AUDIO_NVAL_0960); |
2269 | 2267 | ||
2270 | value = (24000 * 24576) / (128 * sor->audio.sample_rate / 1000); | 2268 | value = (24000 * 24576) / (128 * sor->format.sample_rate / 1000); |
2271 | tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_1920); | 2269 | tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_1920); |
2272 | tegra_sor_writel(sor, 24576, SOR_AUDIO_NVAL_1920); | 2270 | tegra_sor_writel(sor, 24576, SOR_AUDIO_NVAL_1920); |
2273 | 2271 | ||
@@ -2555,7 +2553,7 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) | |||
2555 | 2553 | ||
2556 | /* XXX not in TRM */ | 2554 | /* XXX not in TRM */ |
2557 | for (value = 0, i = 0; i < 5; i++) | 2555 | for (value = 0, i = 0; i < 5; i++) |
2558 | value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->soc->xbar_cfg[i]) | | 2556 | value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->xbar_cfg[i]) | |
2559 | SOR_XBAR_CTRL_LINK1_XSEL(i, i); | 2557 | SOR_XBAR_CTRL_LINK1_XSEL(i, i); |
2560 | 2558 | ||
2561 | tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL); | 2559 | tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL); |
@@ -3176,6 +3174,8 @@ MODULE_DEVICE_TABLE(of, tegra_sor_of_match); | |||
3176 | static int tegra_sor_parse_dt(struct tegra_sor *sor) | 3174 | static int tegra_sor_parse_dt(struct tegra_sor *sor) |
3177 | { | 3175 | { |
3178 | struct device_node *np = sor->dev->of_node; | 3176 | struct device_node *np = sor->dev->of_node; |
3177 | u32 xbar_cfg[5]; | ||
3178 | unsigned int i; | ||
3179 | u32 value; | 3179 | u32 value; |
3180 | int err; | 3180 | int err; |
3181 | 3181 | ||
@@ -3193,25 +3193,18 @@ static int tegra_sor_parse_dt(struct tegra_sor *sor) | |||
3193 | sor->pad = TEGRA_IO_PAD_HDMI_DP0 + sor->index; | 3193 | sor->pad = TEGRA_IO_PAD_HDMI_DP0 + sor->index; |
3194 | } | 3194 | } |
3195 | 3195 | ||
3196 | return 0; | 3196 | err = of_property_read_u32_array(np, "nvidia,xbar-cfg", xbar_cfg, 5); |
3197 | } | 3197 | if (err < 0) { |
3198 | 3198 | /* fall back to default per-SoC XBAR configuration */ | |
3199 | static void tegra_hda_parse_format(unsigned int format, unsigned int *rate, | 3199 | for (i = 0; i < 5; i++) |
3200 | unsigned int *channels) | 3200 | sor->xbar_cfg[i] = sor->soc->xbar_cfg[i]; |
3201 | { | 3201 | } else { |
3202 | unsigned int mul, div; | 3202 | /* copy cells to SOR XBAR configuration */ |
3203 | 3203 | for (i = 0; i < 5; i++) | |
3204 | if (format & AC_FMT_BASE_44K) | 3204 | sor->xbar_cfg[i] = xbar_cfg[i]; |
3205 | *rate = 44100; | 3205 | } |
3206 | else | ||
3207 | *rate = 48000; | ||
3208 | |||
3209 | mul = (format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT; | ||
3210 | div = (format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT; | ||
3211 | |||
3212 | *rate = *rate * (mul + 1) / (div + 1); | ||
3213 | 3206 | ||
3214 | *channels = (format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT; | 3207 | return 0; |
3215 | } | 3208 | } |
3216 | 3209 | ||
3217 | static irqreturn_t tegra_sor_irq(int irq, void *data) | 3210 | static irqreturn_t tegra_sor_irq(int irq, void *data) |
@@ -3226,14 +3219,11 @@ static irqreturn_t tegra_sor_irq(int irq, void *data) | |||
3226 | value = tegra_sor_readl(sor, SOR_AUDIO_HDA_CODEC_SCRATCH0); | 3219 | value = tegra_sor_readl(sor, SOR_AUDIO_HDA_CODEC_SCRATCH0); |
3227 | 3220 | ||
3228 | if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) { | 3221 | if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) { |
3229 | unsigned int format, sample_rate, channels; | 3222 | unsigned int format; |
3230 | 3223 | ||
3231 | format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK; | 3224 | format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK; |
3232 | 3225 | ||
3233 | tegra_hda_parse_format(format, &sample_rate, &channels); | 3226 | tegra_hda_parse_format(format, &sor->format); |
3234 | |||
3235 | sor->audio.sample_rate = sample_rate; | ||
3236 | sor->audio.channels = channels; | ||
3237 | 3227 | ||
3238 | tegra_sor_hdmi_audio_enable(sor); | 3228 | tegra_sor_hdmi_audio_enable(sor); |
3239 | } else { | 3229 | } else { |
diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c index d47983deb1cf..39bfed9623de 100644 --- a/drivers/gpu/drm/tegra/vic.c +++ b/drivers/gpu/drm/tegra/vic.c | |||
@@ -26,6 +26,7 @@ | |||
26 | struct vic_config { | 26 | struct vic_config { |
27 | const char *firmware; | 27 | const char *firmware; |
28 | unsigned int version; | 28 | unsigned int version; |
29 | bool supports_sid; | ||
29 | }; | 30 | }; |
30 | 31 | ||
31 | struct vic { | 32 | struct vic { |
@@ -105,6 +106,22 @@ static int vic_boot(struct vic *vic) | |||
105 | if (vic->booted) | 106 | if (vic->booted) |
106 | return 0; | 107 | return 0; |
107 | 108 | ||
109 | if (vic->config->supports_sid) { | ||
110 | struct iommu_fwspec *spec = dev_iommu_fwspec_get(vic->dev); | ||
111 | u32 value; | ||
112 | |||
113 | value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) | | ||
114 | TRANSCFG_ATT(0, TRANSCFG_SID_HW); | ||
115 | vic_writel(vic, value, VIC_TFBIF_TRANSCFG); | ||
116 | |||
117 | if (spec && spec->num_ids > 0) { | ||
118 | value = spec->ids[0] & 0xffff; | ||
119 | |||
120 | vic_writel(vic, value, VIC_THI_STREAMID0); | ||
121 | vic_writel(vic, value, VIC_THI_STREAMID1); | ||
122 | } | ||
123 | } | ||
124 | |||
108 | /* setup clockgating registers */ | 125 | /* setup clockgating registers */ |
109 | vic_writel(vic, CG_IDLE_CG_DLY_CNT(4) | | 126 | vic_writel(vic, CG_IDLE_CG_DLY_CNT(4) | |
110 | CG_IDLE_CG_EN | | 127 | CG_IDLE_CG_EN | |
@@ -181,13 +198,6 @@ static int vic_init(struct host1x_client *client) | |||
181 | vic->domain = tegra->domain; | 198 | vic->domain = tegra->domain; |
182 | } | 199 | } |
183 | 200 | ||
184 | if (!vic->falcon.data) { | ||
185 | vic->falcon.data = tegra; | ||
186 | err = falcon_load_firmware(&vic->falcon); | ||
187 | if (err < 0) | ||
188 | goto detach; | ||
189 | } | ||
190 | |||
191 | vic->channel = host1x_channel_request(client->dev); | 201 | vic->channel = host1x_channel_request(client->dev); |
192 | if (!vic->channel) { | 202 | if (!vic->channel) { |
193 | err = -ENOMEM; | 203 | err = -ENOMEM; |
@@ -246,6 +256,30 @@ static const struct host1x_client_ops vic_client_ops = { | |||
246 | .exit = vic_exit, | 256 | .exit = vic_exit, |
247 | }; | 257 | }; |
248 | 258 | ||
259 | static int vic_load_firmware(struct vic *vic) | ||
260 | { | ||
261 | int err; | ||
262 | |||
263 | if (vic->falcon.data) | ||
264 | return 0; | ||
265 | |||
266 | vic->falcon.data = vic->client.drm; | ||
267 | |||
268 | err = falcon_read_firmware(&vic->falcon, vic->config->firmware); | ||
269 | if (err < 0) | ||
270 | goto cleanup; | ||
271 | |||
272 | err = falcon_load_firmware(&vic->falcon); | ||
273 | if (err < 0) | ||
274 | goto cleanup; | ||
275 | |||
276 | return 0; | ||
277 | |||
278 | cleanup: | ||
279 | vic->falcon.data = NULL; | ||
280 | return err; | ||
281 | } | ||
282 | |||
249 | static int vic_open_channel(struct tegra_drm_client *client, | 283 | static int vic_open_channel(struct tegra_drm_client *client, |
250 | struct tegra_drm_context *context) | 284 | struct tegra_drm_context *context) |
251 | { | 285 | { |
@@ -256,19 +290,25 @@ static int vic_open_channel(struct tegra_drm_client *client, | |||
256 | if (err < 0) | 290 | if (err < 0) |
257 | return err; | 291 | return err; |
258 | 292 | ||
293 | err = vic_load_firmware(vic); | ||
294 | if (err < 0) | ||
295 | goto rpm_put; | ||
296 | |||
259 | err = vic_boot(vic); | 297 | err = vic_boot(vic); |
260 | if (err < 0) { | 298 | if (err < 0) |
261 | pm_runtime_put(vic->dev); | 299 | goto rpm_put; |
262 | return err; | ||
263 | } | ||
264 | 300 | ||
265 | context->channel = host1x_channel_get(vic->channel); | 301 | context->channel = host1x_channel_get(vic->channel); |
266 | if (!context->channel) { | 302 | if (!context->channel) { |
267 | pm_runtime_put(vic->dev); | 303 | err = -ENOMEM; |
268 | return -ENOMEM; | 304 | goto rpm_put; |
269 | } | 305 | } |
270 | 306 | ||
271 | return 0; | 307 | return 0; |
308 | |||
309 | rpm_put: | ||
310 | pm_runtime_put(vic->dev); | ||
311 | return err; | ||
272 | } | 312 | } |
273 | 313 | ||
274 | static void vic_close_channel(struct tegra_drm_context *context) | 314 | static void vic_close_channel(struct tegra_drm_context *context) |
@@ -291,6 +331,7 @@ static const struct tegra_drm_client_ops vic_ops = { | |||
291 | static const struct vic_config vic_t124_config = { | 331 | static const struct vic_config vic_t124_config = { |
292 | .firmware = NVIDIA_TEGRA_124_VIC_FIRMWARE, | 332 | .firmware = NVIDIA_TEGRA_124_VIC_FIRMWARE, |
293 | .version = 0x40, | 333 | .version = 0x40, |
334 | .supports_sid = false, | ||
294 | }; | 335 | }; |
295 | 336 | ||
296 | #define NVIDIA_TEGRA_210_VIC_FIRMWARE "nvidia/tegra210/vic04_ucode.bin" | 337 | #define NVIDIA_TEGRA_210_VIC_FIRMWARE "nvidia/tegra210/vic04_ucode.bin" |
@@ -298,6 +339,7 @@ static const struct vic_config vic_t124_config = { | |||
298 | static const struct vic_config vic_t210_config = { | 339 | static const struct vic_config vic_t210_config = { |
299 | .firmware = NVIDIA_TEGRA_210_VIC_FIRMWARE, | 340 | .firmware = NVIDIA_TEGRA_210_VIC_FIRMWARE, |
300 | .version = 0x21, | 341 | .version = 0x21, |
342 | .supports_sid = false, | ||
301 | }; | 343 | }; |
302 | 344 | ||
303 | #define NVIDIA_TEGRA_186_VIC_FIRMWARE "nvidia/tegra186/vic04_ucode.bin" | 345 | #define NVIDIA_TEGRA_186_VIC_FIRMWARE "nvidia/tegra186/vic04_ucode.bin" |
@@ -305,6 +347,7 @@ static const struct vic_config vic_t210_config = { | |||
305 | static const struct vic_config vic_t186_config = { | 347 | static const struct vic_config vic_t186_config = { |
306 | .firmware = NVIDIA_TEGRA_186_VIC_FIRMWARE, | 348 | .firmware = NVIDIA_TEGRA_186_VIC_FIRMWARE, |
307 | .version = 0x18, | 349 | .version = 0x18, |
350 | .supports_sid = true, | ||
308 | }; | 351 | }; |
309 | 352 | ||
310 | #define NVIDIA_TEGRA_194_VIC_FIRMWARE "nvidia/tegra194/vic.bin" | 353 | #define NVIDIA_TEGRA_194_VIC_FIRMWARE "nvidia/tegra194/vic.bin" |
@@ -312,6 +355,7 @@ static const struct vic_config vic_t186_config = { | |||
312 | static const struct vic_config vic_t194_config = { | 355 | static const struct vic_config vic_t194_config = { |
313 | .firmware = NVIDIA_TEGRA_194_VIC_FIRMWARE, | 356 | .firmware = NVIDIA_TEGRA_194_VIC_FIRMWARE, |
314 | .version = 0x19, | 357 | .version = 0x19, |
358 | .supports_sid = true, | ||
315 | }; | 359 | }; |
316 | 360 | ||
317 | static const struct of_device_id vic_match[] = { | 361 | static const struct of_device_id vic_match[] = { |
@@ -372,10 +416,6 @@ static int vic_probe(struct platform_device *pdev) | |||
372 | if (err < 0) | 416 | if (err < 0) |
373 | return err; | 417 | return err; |
374 | 418 | ||
375 | err = falcon_read_firmware(&vic->falcon, vic->config->firmware); | ||
376 | if (err < 0) | ||
377 | goto exit_falcon; | ||
378 | |||
379 | platform_set_drvdata(pdev, vic); | 419 | platform_set_drvdata(pdev, vic); |
380 | 420 | ||
381 | INIT_LIST_HEAD(&vic->client.base.list); | 421 | INIT_LIST_HEAD(&vic->client.base.list); |
@@ -393,7 +433,6 @@ static int vic_probe(struct platform_device *pdev) | |||
393 | err = host1x_client_register(&vic->client.base); | 433 | err = host1x_client_register(&vic->client.base); |
394 | if (err < 0) { | 434 | if (err < 0) { |
395 | dev_err(dev, "failed to register host1x client: %d\n", err); | 435 | dev_err(dev, "failed to register host1x client: %d\n", err); |
396 | platform_set_drvdata(pdev, NULL); | ||
397 | goto exit_falcon; | 436 | goto exit_falcon; |
398 | } | 437 | } |
399 | 438 | ||
diff --git a/drivers/gpu/drm/tegra/vic.h b/drivers/gpu/drm/tegra/vic.h index 21844817a7e1..017584340dd6 100644 --- a/drivers/gpu/drm/tegra/vic.h +++ b/drivers/gpu/drm/tegra/vic.h | |||
@@ -17,11 +17,20 @@ | |||
17 | 17 | ||
18 | /* VIC registers */ | 18 | /* VIC registers */ |
19 | 19 | ||
20 | #define VIC_THI_STREAMID0 0x00000030 | ||
21 | #define VIC_THI_STREAMID1 0x00000034 | ||
22 | |||
20 | #define NV_PVIC_MISC_PRI_VIC_CG 0x000016d0 | 23 | #define NV_PVIC_MISC_PRI_VIC_CG 0x000016d0 |
21 | #define CG_IDLE_CG_DLY_CNT(val) ((val & 0x3f) << 0) | 24 | #define CG_IDLE_CG_DLY_CNT(val) ((val & 0x3f) << 0) |
22 | #define CG_IDLE_CG_EN (1 << 6) | 25 | #define CG_IDLE_CG_EN (1 << 6) |
23 | #define CG_WAKEUP_DLY_CNT(val) ((val & 0xf) << 16) | 26 | #define CG_WAKEUP_DLY_CNT(val) ((val & 0xf) << 16) |
24 | 27 | ||
28 | #define VIC_TFBIF_TRANSCFG 0x00002044 | ||
29 | #define TRANSCFG_ATT(i, v) (((v) & 0x3) << (i * 4)) | ||
30 | #define TRANSCFG_SID_HW 0 | ||
31 | #define TRANSCFG_SID_PHY 1 | ||
32 | #define TRANSCFG_SID_FALCON 2 | ||
33 | |||
25 | /* Firmware offsets */ | 34 | /* Firmware offsets */ |
26 | 35 | ||
27 | #define VIC_UCODE_FCE_HEADER_OFFSET (6*4) | 36 | #define VIC_UCODE_FCE_HEADER_OFFSET (6*4) |
diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index b4c385d4a6af..103fffc1904b 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c | |||
@@ -15,8 +15,10 @@ | |||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include <linux/debugfs.h> | ||
18 | #include <linux/host1x.h> | 19 | #include <linux/host1x.h> |
19 | #include <linux/of.h> | 20 | #include <linux/of.h> |
21 | #include <linux/seq_file.h> | ||
20 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
21 | #include <linux/of_device.h> | 23 | #include <linux/of_device.h> |
22 | 24 | ||
@@ -500,6 +502,36 @@ static void host1x_detach_driver(struct host1x *host1x, | |||
500 | mutex_unlock(&host1x->devices_lock); | 502 | mutex_unlock(&host1x->devices_lock); |
501 | } | 503 | } |
502 | 504 | ||
505 | static int host1x_devices_show(struct seq_file *s, void *data) | ||
506 | { | ||
507 | struct host1x *host1x = s->private; | ||
508 | struct host1x_device *device; | ||
509 | |||
510 | mutex_lock(&host1x->devices_lock); | ||
511 | |||
512 | list_for_each_entry(device, &host1x->devices, list) { | ||
513 | struct host1x_subdev *subdev; | ||
514 | |||
515 | seq_printf(s, "%s\n", dev_name(&device->dev)); | ||
516 | |||
517 | mutex_lock(&device->subdevs_lock); | ||
518 | |||
519 | list_for_each_entry(subdev, &device->active, list) | ||
520 | seq_printf(s, " %pOFf: %s\n", subdev->np, | ||
521 | dev_name(subdev->client->dev)); | ||
522 | |||
523 | list_for_each_entry(subdev, &device->subdevs, list) | ||
524 | seq_printf(s, " %pOFf:\n", subdev->np); | ||
525 | |||
526 | mutex_unlock(&device->subdevs_lock); | ||
527 | } | ||
528 | |||
529 | mutex_unlock(&host1x->devices_lock); | ||
530 | |||
531 | return 0; | ||
532 | } | ||
533 | DEFINE_SHOW_ATTRIBUTE(host1x_devices); | ||
534 | |||
503 | /** | 535 | /** |
504 | * host1x_register() - register a host1x controller | 536 | * host1x_register() - register a host1x controller |
505 | * @host1x: host1x controller | 537 | * @host1x: host1x controller |
@@ -523,6 +555,9 @@ int host1x_register(struct host1x *host1x) | |||
523 | 555 | ||
524 | mutex_unlock(&drivers_lock); | 556 | mutex_unlock(&drivers_lock); |
525 | 557 | ||
558 | debugfs_create_file("devices", S_IRUGO, host1x->debugfs, host1x, | ||
559 | &host1x_devices_fops); | ||
560 | |||
526 | return 0; | 561 | return 0; |
527 | } | 562 | } |
528 | 563 | ||
diff --git a/drivers/gpu/host1x/cdma.c b/drivers/gpu/host1x/cdma.c index 91df51e631b2..f45b7c69b694 100644 --- a/drivers/gpu/host1x/cdma.c +++ b/drivers/gpu/host1x/cdma.c | |||
@@ -41,7 +41,17 @@ | |||
41 | * means that the push buffer is full, not empty. | 41 | * means that the push buffer is full, not empty. |
42 | */ | 42 | */ |
43 | 43 | ||
44 | #define HOST1X_PUSHBUFFER_SLOTS 512 | 44 | /* |
45 | * Typically the commands written into the push buffer are a pair of words. We | ||
46 | * use slots to represent each of these pairs and to simplify things. Note the | ||
47 | * strange number of slots allocated here. 512 slots will fit exactly within a | ||
48 | * single memory page. We also need one additional word at the end of the push | ||
49 | * buffer for the RESTART opcode that will instruct the CDMA to jump back to | ||
50 | * the beginning of the push buffer. With 512 slots, this means that we'll use | ||
51 | * 2 memory pages and waste 4092 bytes of the second page that will never be | ||
52 | * used. | ||
53 | */ | ||
54 | #define HOST1X_PUSHBUFFER_SLOTS 511 | ||
45 | 55 | ||
46 | /* | 56 | /* |
47 | * Clean up push buffer resources | 57 | * Clean up push buffer resources |
@@ -143,7 +153,10 @@ static void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2) | |||
143 | WARN_ON(pb->pos == pb->fence); | 153 | WARN_ON(pb->pos == pb->fence); |
144 | *(p++) = op1; | 154 | *(p++) = op1; |
145 | *(p++) = op2; | 155 | *(p++) = op2; |
146 | pb->pos = (pb->pos + 8) & (pb->size - 1); | 156 | pb->pos += 8; |
157 | |||
158 | if (pb->pos >= pb->size) | ||
159 | pb->pos -= pb->size; | ||
147 | } | 160 | } |
148 | 161 | ||
149 | /* | 162 | /* |
@@ -153,7 +166,10 @@ static void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2) | |||
153 | static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots) | 166 | static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots) |
154 | { | 167 | { |
155 | /* Advance the next write position */ | 168 | /* Advance the next write position */ |
156 | pb->fence = (pb->fence + slots * 8) & (pb->size - 1); | 169 | pb->fence += slots * 8; |
170 | |||
171 | if (pb->fence >= pb->size) | ||
172 | pb->fence -= pb->size; | ||
157 | } | 173 | } |
158 | 174 | ||
159 | /* | 175 | /* |
@@ -161,7 +177,12 @@ static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots) | |||
161 | */ | 177 | */ |
162 | static u32 host1x_pushbuffer_space(struct push_buffer *pb) | 178 | static u32 host1x_pushbuffer_space(struct push_buffer *pb) |
163 | { | 179 | { |
164 | return ((pb->fence - pb->pos) & (pb->size - 1)) / 8; | 180 | unsigned int fence = pb->fence; |
181 | |||
182 | if (pb->fence < pb->pos) | ||
183 | fence += pb->size; | ||
184 | |||
185 | return (fence - pb->pos) / 8; | ||
165 | } | 186 | } |
166 | 187 | ||
167 | /* | 188 | /* |
@@ -210,7 +231,7 @@ unsigned int host1x_cdma_wait_locked(struct host1x_cdma *cdma, | |||
210 | cdma->event = event; | 231 | cdma->event = event; |
211 | 232 | ||
212 | mutex_unlock(&cdma->lock); | 233 | mutex_unlock(&cdma->lock); |
213 | down(&cdma->sem); | 234 | wait_for_completion(&cdma->complete); |
214 | mutex_lock(&cdma->lock); | 235 | mutex_lock(&cdma->lock); |
215 | } | 236 | } |
216 | 237 | ||
@@ -218,6 +239,45 @@ unsigned int host1x_cdma_wait_locked(struct host1x_cdma *cdma, | |||
218 | } | 239 | } |
219 | 240 | ||
220 | /* | 241 | /* |
242 | * Sleep (if necessary) until the push buffer has enough free space. | ||
243 | * | ||
244 | * Must be called with the cdma lock held. | ||
245 | */ | ||
246 | int host1x_cdma_wait_pushbuffer_space(struct host1x *host1x, | ||
247 | struct host1x_cdma *cdma, | ||
248 | unsigned int needed) | ||
249 | { | ||
250 | while (true) { | ||
251 | struct push_buffer *pb = &cdma->push_buffer; | ||
252 | unsigned int space; | ||
253 | |||
254 | space = host1x_pushbuffer_space(pb); | ||
255 | if (space >= needed) | ||
256 | break; | ||
257 | |||
258 | trace_host1x_wait_cdma(dev_name(cdma_to_channel(cdma)->dev), | ||
259 | CDMA_EVENT_PUSH_BUFFER_SPACE); | ||
260 | |||
261 | host1x_hw_cdma_flush(host1x, cdma); | ||
262 | |||
263 | /* If somebody has managed to already start waiting, yield */ | ||
264 | if (cdma->event != CDMA_EVENT_NONE) { | ||
265 | mutex_unlock(&cdma->lock); | ||
266 | schedule(); | ||
267 | mutex_lock(&cdma->lock); | ||
268 | continue; | ||
269 | } | ||
270 | |||
271 | cdma->event = CDMA_EVENT_PUSH_BUFFER_SPACE; | ||
272 | |||
273 | mutex_unlock(&cdma->lock); | ||
274 | wait_for_completion(&cdma->complete); | ||
275 | mutex_lock(&cdma->lock); | ||
276 | } | ||
277 | |||
278 | return 0; | ||
279 | } | ||
280 | /* | ||
221 | * Start timer that tracks the time spent by the job. | 281 | * Start timer that tracks the time spent by the job. |
222 | * Must be called with the cdma lock held. | 282 | * Must be called with the cdma lock held. |
223 | */ | 283 | */ |
@@ -314,7 +374,7 @@ static void update_cdma_locked(struct host1x_cdma *cdma) | |||
314 | 374 | ||
315 | if (signal) { | 375 | if (signal) { |
316 | cdma->event = CDMA_EVENT_NONE; | 376 | cdma->event = CDMA_EVENT_NONE; |
317 | up(&cdma->sem); | 377 | complete(&cdma->complete); |
318 | } | 378 | } |
319 | } | 379 | } |
320 | 380 | ||
@@ -323,7 +383,7 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma, | |||
323 | { | 383 | { |
324 | struct host1x *host1x = cdma_to_host1x(cdma); | 384 | struct host1x *host1x = cdma_to_host1x(cdma); |
325 | u32 restart_addr, syncpt_incrs, syncpt_val; | 385 | u32 restart_addr, syncpt_incrs, syncpt_val; |
326 | struct host1x_job *job = NULL; | 386 | struct host1x_job *job, *next_job = NULL; |
327 | 387 | ||
328 | syncpt_val = host1x_syncpt_load(cdma->timeout.syncpt); | 388 | syncpt_val = host1x_syncpt_load(cdma->timeout.syncpt); |
329 | 389 | ||
@@ -341,40 +401,37 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma, | |||
341 | __func__); | 401 | __func__); |
342 | 402 | ||
343 | list_for_each_entry(job, &cdma->sync_queue, list) { | 403 | list_for_each_entry(job, &cdma->sync_queue, list) { |
344 | if (syncpt_val < job->syncpt_end) | 404 | if (syncpt_val < job->syncpt_end) { |
345 | break; | 405 | |
406 | if (!list_is_last(&job->list, &cdma->sync_queue)) | ||
407 | next_job = list_next_entry(job, list); | ||
408 | |||
409 | goto syncpt_incr; | ||
410 | } | ||
346 | 411 | ||
347 | host1x_job_dump(dev, job); | 412 | host1x_job_dump(dev, job); |
348 | } | 413 | } |
349 | 414 | ||
415 | /* all jobs have been completed */ | ||
416 | job = NULL; | ||
417 | |||
418 | syncpt_incr: | ||
419 | |||
350 | /* | 420 | /* |
351 | * Walk the sync_queue, first incrementing with the CPU syncpts that | 421 | * Increment with CPU the remaining syncpts of a partially executed job. |
352 | * are partially executed (the first buffer) or fully skipped while | ||
353 | * still in the current context (slots are also NOP-ed). | ||
354 | * | 422 | * |
355 | * At the point contexts are interleaved, syncpt increments must be | 423 | * CDMA will continue execution starting with the next job or will get |
356 | * done inline with the pushbuffer from a GATHER buffer to maintain | 424 | * into idle state. |
357 | * the order (slots are modified to be a GATHER of syncpt incrs). | ||
358 | * | ||
359 | * Note: save in restart_addr the location where the timed out buffer | ||
360 | * started in the PB, so we can start the refetch from there (with the | ||
361 | * modified NOP-ed PB slots). This lets things appear to have completed | ||
362 | * properly for this buffer and resources are freed. | ||
363 | */ | 425 | */ |
364 | 426 | if (next_job) | |
365 | dev_dbg(dev, "%s: perform CPU incr on pending same ctx buffers\n", | 427 | restart_addr = next_job->first_get; |
366 | __func__); | ||
367 | |||
368 | if (!list_empty(&cdma->sync_queue)) | ||
369 | restart_addr = job->first_get; | ||
370 | else | 428 | else |
371 | restart_addr = cdma->last_pos; | 429 | restart_addr = cdma->last_pos; |
372 | 430 | ||
373 | /* do CPU increments as long as this context continues */ | 431 | /* do CPU increments for the remaining syncpts */ |
374 | list_for_each_entry_from(job, &cdma->sync_queue, list) { | 432 | if (job) { |
375 | /* different context, gets us out of this loop */ | 433 | dev_dbg(dev, "%s: perform CPU incr on pending buffers\n", |
376 | if (job->client != cdma->timeout.client) | 434 | __func__); |
377 | break; | ||
378 | 435 | ||
379 | /* won't need a timeout when replayed */ | 436 | /* won't need a timeout when replayed */ |
380 | job->timeout = 0; | 437 | job->timeout = 0; |
@@ -389,21 +446,10 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma, | |||
389 | syncpt_incrs, job->syncpt_end, | 446 | syncpt_incrs, job->syncpt_end, |
390 | job->num_slots); | 447 | job->num_slots); |
391 | 448 | ||
392 | syncpt_val += syncpt_incrs; | 449 | dev_dbg(dev, "%s: finished sync_queue modification\n", |
450 | __func__); | ||
393 | } | 451 | } |
394 | 452 | ||
395 | /* | ||
396 | * The following sumbits from the same client may be dependent on the | ||
397 | * failed submit and therefore they may fail. Force a small timeout | ||
398 | * to make the queue cleanup faster. | ||
399 | */ | ||
400 | |||
401 | list_for_each_entry_from(job, &cdma->sync_queue, list) | ||
402 | if (job->client == cdma->timeout.client) | ||
403 | job->timeout = min_t(unsigned int, job->timeout, 500); | ||
404 | |||
405 | dev_dbg(dev, "%s: finished sync_queue modification\n", __func__); | ||
406 | |||
407 | /* roll back DMAGET and start up channel again */ | 453 | /* roll back DMAGET and start up channel again */ |
408 | host1x_hw_cdma_resume(host1x, cdma, restart_addr); | 454 | host1x_hw_cdma_resume(host1x, cdma, restart_addr); |
409 | } | 455 | } |
@@ -416,7 +462,7 @@ int host1x_cdma_init(struct host1x_cdma *cdma) | |||
416 | int err; | 462 | int err; |
417 | 463 | ||
418 | mutex_init(&cdma->lock); | 464 | mutex_init(&cdma->lock); |
419 | sema_init(&cdma->sem, 0); | 465 | init_completion(&cdma->complete); |
420 | 466 | ||
421 | INIT_LIST_HEAD(&cdma->sync_queue); | 467 | INIT_LIST_HEAD(&cdma->sync_queue); |
422 | 468 | ||
@@ -510,6 +556,59 @@ void host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2) | |||
510 | } | 556 | } |
511 | 557 | ||
512 | /* | 558 | /* |
559 | * Push four words into two consecutive push buffer slots. Note that extra | ||
560 | * care needs to be taken not to split the two slots across the end of the | ||
561 | * push buffer. Otherwise the RESTART opcode at the end of the push buffer | ||
562 | * that ensures processing will restart at the beginning will break up the | ||
563 | * four words. | ||
564 | * | ||
565 | * Blocks as necessary if the push buffer is full. | ||
566 | */ | ||
567 | void host1x_cdma_push_wide(struct host1x_cdma *cdma, u32 op1, u32 op2, | ||
568 | u32 op3, u32 op4) | ||
569 | { | ||
570 | struct host1x_channel *channel = cdma_to_channel(cdma); | ||
571 | struct host1x *host1x = cdma_to_host1x(cdma); | ||
572 | struct push_buffer *pb = &cdma->push_buffer; | ||
573 | unsigned int needed = 2, extra = 0, i; | ||
574 | unsigned int space = cdma->slots_free; | ||
575 | |||
576 | if (host1x_debug_trace_cmdbuf) | ||
577 | trace_host1x_cdma_push_wide(dev_name(channel->dev), op1, op2, | ||
578 | op3, op4); | ||
579 | |||
580 | /* compute number of extra slots needed for padding */ | ||
581 | if (pb->pos + 16 > pb->size) { | ||
582 | extra = (pb->size - pb->pos) / 8; | ||
583 | needed += extra; | ||
584 | } | ||
585 | |||
586 | host1x_cdma_wait_pushbuffer_space(host1x, cdma, needed); | ||
587 | space = host1x_pushbuffer_space(pb); | ||
588 | |||
589 | cdma->slots_free = space - needed; | ||
590 | cdma->slots_used += needed; | ||
591 | |||
592 | /* | ||
593 | * Note that we rely on the fact that this is only used to submit wide | ||
594 | * gather opcodes, which consist of 3 words, and they are padded with | ||
595 | * a NOP to avoid having to deal with fractional slots (a slot always | ||
596 | * represents 2 words). The fourth opcode passed to this function will | ||
597 | * therefore always be a NOP. | ||
598 | * | ||
599 | * This works around a slight ambiguity when it comes to opcodes. For | ||
600 | * all current host1x incarnations the NOP opcode uses the exact same | ||
601 | * encoding (0x20000000), so we could hard-code the value here, but a | ||
602 | * new incarnation may change it and break that assumption. | ||
603 | */ | ||
604 | for (i = 0; i < extra; i++) | ||
605 | host1x_pushbuffer_push(pb, op4, op4); | ||
606 | |||
607 | host1x_pushbuffer_push(pb, op1, op2); | ||
608 | host1x_pushbuffer_push(pb, op3, op4); | ||
609 | } | ||
610 | |||
611 | /* | ||
513 | * End a cdma submit | 612 | * End a cdma submit |
514 | * Kick off DMA, add job to the sync queue, and a number of slots to be freed | 613 | * Kick off DMA, add job to the sync queue, and a number of slots to be freed |
515 | * from the pushbuffer. The handles for a submit must all be pinned at the same | 614 | * from the pushbuffer. The handles for a submit must all be pinned at the same |
diff --git a/drivers/gpu/host1x/cdma.h b/drivers/gpu/host1x/cdma.h index e97e17b82370..3a5e0408b8d1 100644 --- a/drivers/gpu/host1x/cdma.h +++ b/drivers/gpu/host1x/cdma.h | |||
@@ -20,7 +20,7 @@ | |||
20 | #define __HOST1X_CDMA_H | 20 | #define __HOST1X_CDMA_H |
21 | 21 | ||
22 | #include <linux/sched.h> | 22 | #include <linux/sched.h> |
23 | #include <linux/semaphore.h> | 23 | #include <linux/completion.h> |
24 | #include <linux/list.h> | 24 | #include <linux/list.h> |
25 | 25 | ||
26 | struct host1x_syncpt; | 26 | struct host1x_syncpt; |
@@ -69,8 +69,8 @@ enum cdma_event { | |||
69 | 69 | ||
70 | struct host1x_cdma { | 70 | struct host1x_cdma { |
71 | struct mutex lock; /* controls access to shared state */ | 71 | struct mutex lock; /* controls access to shared state */ |
72 | struct semaphore sem; /* signalled when event occurs */ | 72 | struct completion complete; /* signalled when event occurs */ |
73 | enum cdma_event event; /* event that sem is waiting for */ | 73 | enum cdma_event event; /* event that complete is waiting for */ |
74 | unsigned int slots_used; /* pb slots used in current submit */ | 74 | unsigned int slots_used; /* pb slots used in current submit */ |
75 | unsigned int slots_free; /* pb slots free in current submit */ | 75 | unsigned int slots_free; /* pb slots free in current submit */ |
76 | unsigned int first_get; /* DMAGET value, where submit begins */ | 76 | unsigned int first_get; /* DMAGET value, where submit begins */ |
@@ -90,6 +90,8 @@ 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 | 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); |
92 | 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); |
93 | void host1x_cdma_push_wide(struct host1x_cdma *cdma, u32 op1, u32 op2, | ||
94 | u32 op3, u32 op4); | ||
93 | void host1x_cdma_end(struct host1x_cdma *cdma, struct host1x_job *job); | 95 | void host1x_cdma_end(struct host1x_cdma *cdma, struct host1x_job *job); |
94 | void host1x_cdma_update(struct host1x_cdma *cdma); | 96 | void host1x_cdma_update(struct host1x_cdma *cdma); |
95 | void host1x_cdma_peek(struct host1x_cdma *cdma, u32 dmaget, int slot, | 97 | void host1x_cdma_peek(struct host1x_cdma *cdma, u32 dmaget, int slot, |
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 419d8929a98f..ee3c7b81a29d 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c | |||
@@ -120,6 +120,15 @@ static const struct host1x_info host1x05_info = { | |||
120 | .dma_mask = DMA_BIT_MASK(34), | 120 | .dma_mask = DMA_BIT_MASK(34), |
121 | }; | 121 | }; |
122 | 122 | ||
123 | static const struct host1x_sid_entry tegra186_sid_table[] = { | ||
124 | { | ||
125 | /* VIC */ | ||
126 | .base = 0x1af0, | ||
127 | .offset = 0x30, | ||
128 | .limit = 0x34 | ||
129 | }, | ||
130 | }; | ||
131 | |||
123 | static const struct host1x_info host1x06_info = { | 132 | static const struct host1x_info host1x06_info = { |
124 | .nb_channels = 63, | 133 | .nb_channels = 63, |
125 | .nb_pts = 576, | 134 | .nb_pts = 576, |
@@ -127,8 +136,19 @@ static const struct host1x_info host1x06_info = { | |||
127 | .nb_bases = 16, | 136 | .nb_bases = 16, |
128 | .init = host1x06_init, | 137 | .init = host1x06_init, |
129 | .sync_offset = 0x0, | 138 | .sync_offset = 0x0, |
130 | .dma_mask = DMA_BIT_MASK(34), | 139 | .dma_mask = DMA_BIT_MASK(40), |
131 | .has_hypervisor = true, | 140 | .has_hypervisor = true, |
141 | .num_sid_entries = ARRAY_SIZE(tegra186_sid_table), | ||
142 | .sid_table = tegra186_sid_table, | ||
143 | }; | ||
144 | |||
145 | static const struct host1x_sid_entry tegra194_sid_table[] = { | ||
146 | { | ||
147 | /* VIC */ | ||
148 | .base = 0x1af0, | ||
149 | .offset = 0x30, | ||
150 | .limit = 0x34 | ||
151 | }, | ||
132 | }; | 152 | }; |
133 | 153 | ||
134 | static const struct host1x_info host1x07_info = { | 154 | static const struct host1x_info host1x07_info = { |
@@ -140,6 +160,8 @@ static const struct host1x_info host1x07_info = { | |||
140 | .sync_offset = 0x0, | 160 | .sync_offset = 0x0, |
141 | .dma_mask = DMA_BIT_MASK(40), | 161 | .dma_mask = DMA_BIT_MASK(40), |
142 | .has_hypervisor = true, | 162 | .has_hypervisor = true, |
163 | .num_sid_entries = ARRAY_SIZE(tegra194_sid_table), | ||
164 | .sid_table = tegra194_sid_table, | ||
143 | }; | 165 | }; |
144 | 166 | ||
145 | static const struct of_device_id host1x_of_match[] = { | 167 | static const struct of_device_id host1x_of_match[] = { |
@@ -154,6 +176,19 @@ static const struct of_device_id host1x_of_match[] = { | |||
154 | }; | 176 | }; |
155 | MODULE_DEVICE_TABLE(of, host1x_of_match); | 177 | MODULE_DEVICE_TABLE(of, host1x_of_match); |
156 | 178 | ||
179 | static void host1x_setup_sid_table(struct host1x *host) | ||
180 | { | ||
181 | const struct host1x_info *info = host->info; | ||
182 | unsigned int i; | ||
183 | |||
184 | for (i = 0; i < info->num_sid_entries; i++) { | ||
185 | const struct host1x_sid_entry *entry = &info->sid_table[i]; | ||
186 | |||
187 | host1x_hypervisor_writel(host, entry->offset, entry->base); | ||
188 | host1x_hypervisor_writel(host, entry->limit, entry->base + 4); | ||
189 | } | ||
190 | } | ||
191 | |||
157 | static int host1x_probe(struct platform_device *pdev) | 192 | static int host1x_probe(struct platform_device *pdev) |
158 | { | 193 | { |
159 | struct host1x *host; | 194 | struct host1x *host; |
@@ -248,6 +283,8 @@ static int host1x_probe(struct platform_device *pdev) | |||
248 | host->group = iommu_group_get(&pdev->dev); | 283 | host->group = iommu_group_get(&pdev->dev); |
249 | if (host->group) { | 284 | if (host->group) { |
250 | struct iommu_domain_geometry *geometry; | 285 | struct iommu_domain_geometry *geometry; |
286 | u64 mask = dma_get_mask(host->dev); | ||
287 | dma_addr_t start, end; | ||
251 | unsigned long order; | 288 | unsigned long order; |
252 | 289 | ||
253 | err = iova_cache_get(); | 290 | err = iova_cache_get(); |
@@ -275,11 +312,12 @@ static int host1x_probe(struct platform_device *pdev) | |||
275 | } | 312 | } |
276 | 313 | ||
277 | geometry = &host->domain->geometry; | 314 | geometry = &host->domain->geometry; |
315 | start = geometry->aperture_start & mask; | ||
316 | end = geometry->aperture_end & mask; | ||
278 | 317 | ||
279 | order = __ffs(host->domain->pgsize_bitmap); | 318 | order = __ffs(host->domain->pgsize_bitmap); |
280 | init_iova_domain(&host->iova, 1UL << order, | 319 | init_iova_domain(&host->iova, 1UL << order, start >> order); |
281 | geometry->aperture_start >> order); | 320 | host->iova_end = end; |
282 | host->iova_end = geometry->aperture_end; | ||
283 | } | 321 | } |
284 | 322 | ||
285 | skip_iommu: | 323 | skip_iommu: |
@@ -316,6 +354,9 @@ skip_iommu: | |||
316 | 354 | ||
317 | host1x_debug_init(host); | 355 | host1x_debug_init(host); |
318 | 356 | ||
357 | if (host->info->has_hypervisor) | ||
358 | host1x_setup_sid_table(host); | ||
359 | |||
319 | err = host1x_register(host); | 360 | err = host1x_register(host); |
320 | if (err < 0) | 361 | if (err < 0) |
321 | goto fail_deinit_intr; | 362 | goto fail_deinit_intr; |
diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h index 36f44ffebe73..05216a7e4830 100644 --- a/drivers/gpu/host1x/dev.h +++ b/drivers/gpu/host1x/dev.h | |||
@@ -94,6 +94,12 @@ struct host1x_intr_ops { | |||
94 | int (*free_syncpt_irq)(struct host1x *host); | 94 | int (*free_syncpt_irq)(struct host1x *host); |
95 | }; | 95 | }; |
96 | 96 | ||
97 | struct host1x_sid_entry { | ||
98 | unsigned int base; | ||
99 | unsigned int offset; | ||
100 | unsigned int limit; | ||
101 | }; | ||
102 | |||
97 | struct host1x_info { | 103 | struct host1x_info { |
98 | unsigned int nb_channels; /* host1x: number of channels supported */ | 104 | unsigned int nb_channels; /* host1x: number of channels supported */ |
99 | unsigned int nb_pts; /* host1x: number of syncpoints supported */ | 105 | unsigned int nb_pts; /* host1x: number of syncpoints supported */ |
@@ -103,6 +109,8 @@ struct host1x_info { | |||
103 | unsigned int sync_offset; /* offset of syncpoint registers */ | 109 | unsigned int sync_offset; /* offset of syncpoint registers */ |
104 | u64 dma_mask; /* mask of addressable memory */ | 110 | u64 dma_mask; /* mask of addressable memory */ |
105 | bool has_hypervisor; /* has hypervisor registers */ | 111 | bool has_hypervisor; /* has hypervisor registers */ |
112 | unsigned int num_sid_entries; | ||
113 | const struct host1x_sid_entry *sid_table; | ||
106 | }; | 114 | }; |
107 | 115 | ||
108 | struct host1x { | 116 | struct host1x { |
diff --git a/drivers/gpu/host1x/hw/cdma_hw.c b/drivers/gpu/host1x/hw/cdma_hw.c index ce320534cbed..5d61088db2bb 100644 --- a/drivers/gpu/host1x/hw/cdma_hw.c +++ b/drivers/gpu/host1x/hw/cdma_hw.c | |||
@@ -39,8 +39,6 @@ static void push_buffer_init(struct push_buffer *pb) | |||
39 | static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr, | 39 | static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr, |
40 | u32 syncpt_incrs, u32 syncval, u32 nr_slots) | 40 | u32 syncpt_incrs, u32 syncval, u32 nr_slots) |
41 | { | 41 | { |
42 | struct host1x *host1x = cdma_to_host1x(cdma); | ||
43 | struct push_buffer *pb = &cdma->push_buffer; | ||
44 | unsigned int i; | 42 | unsigned int i; |
45 | 43 | ||
46 | for (i = 0; i < syncpt_incrs; i++) | 44 | for (i = 0; i < syncpt_incrs; i++) |
@@ -48,18 +46,6 @@ static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr, | |||
48 | 46 | ||
49 | /* after CPU incr, ensure shadow is up to date */ | 47 | /* after CPU incr, ensure shadow is up to date */ |
50 | host1x_syncpt_load(cdma->timeout.syncpt); | 48 | host1x_syncpt_load(cdma->timeout.syncpt); |
51 | |||
52 | /* NOP all the PB slots */ | ||
53 | while (nr_slots--) { | ||
54 | u32 *p = (u32 *)(pb->mapped + getptr); | ||
55 | *(p++) = HOST1X_OPCODE_NOP; | ||
56 | *(p++) = HOST1X_OPCODE_NOP; | ||
57 | dev_dbg(host1x->dev, "%s: NOP at %pad+%#x\n", __func__, | ||
58 | &pb->dma, getptr); | ||
59 | getptr = (getptr + 8) & (pb->size - 1); | ||
60 | } | ||
61 | |||
62 | wmb(); | ||
63 | } | 49 | } |
64 | 50 | ||
65 | /* | 51 | /* |
@@ -68,20 +54,31 @@ static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr, | |||
68 | static void cdma_start(struct host1x_cdma *cdma) | 54 | static void cdma_start(struct host1x_cdma *cdma) |
69 | { | 55 | { |
70 | struct host1x_channel *ch = cdma_to_channel(cdma); | 56 | struct host1x_channel *ch = cdma_to_channel(cdma); |
57 | u64 start, end; | ||
71 | 58 | ||
72 | if (cdma->running) | 59 | if (cdma->running) |
73 | return; | 60 | return; |
74 | 61 | ||
75 | cdma->last_pos = cdma->push_buffer.pos; | 62 | cdma->last_pos = cdma->push_buffer.pos; |
63 | start = cdma->push_buffer.dma; | ||
64 | end = cdma->push_buffer.size + 4; | ||
76 | 65 | ||
77 | host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP, | 66 | host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP, |
78 | HOST1X_CHANNEL_DMACTRL); | 67 | HOST1X_CHANNEL_DMACTRL); |
79 | 68 | ||
80 | /* set base, put and end pointer */ | 69 | /* set base, put and end pointer */ |
81 | host1x_ch_writel(ch, cdma->push_buffer.dma, HOST1X_CHANNEL_DMASTART); | 70 | host1x_ch_writel(ch, lower_32_bits(start), HOST1X_CHANNEL_DMASTART); |
71 | #if HOST1X_HW >= 6 | ||
72 | host1x_ch_writel(ch, upper_32_bits(start), HOST1X_CHANNEL_DMASTART_HI); | ||
73 | #endif | ||
82 | host1x_ch_writel(ch, cdma->push_buffer.pos, HOST1X_CHANNEL_DMAPUT); | 74 | host1x_ch_writel(ch, cdma->push_buffer.pos, HOST1X_CHANNEL_DMAPUT); |
83 | host1x_ch_writel(ch, cdma->push_buffer.dma + cdma->push_buffer.size + 4, | 75 | #if HOST1X_HW >= 6 |
84 | HOST1X_CHANNEL_DMAEND); | 76 | host1x_ch_writel(ch, 0, HOST1X_CHANNEL_DMAPUT_HI); |
77 | #endif | ||
78 | host1x_ch_writel(ch, lower_32_bits(end), HOST1X_CHANNEL_DMAEND); | ||
79 | #if HOST1X_HW >= 6 | ||
80 | host1x_ch_writel(ch, upper_32_bits(end), HOST1X_CHANNEL_DMAEND_HI); | ||
81 | #endif | ||
85 | 82 | ||
86 | /* reset GET */ | 83 | /* reset GET */ |
87 | host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP | | 84 | host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP | |
@@ -104,6 +101,7 @@ static void cdma_timeout_restart(struct host1x_cdma *cdma, u32 getptr) | |||
104 | { | 101 | { |
105 | struct host1x *host1x = cdma_to_host1x(cdma); | 102 | struct host1x *host1x = cdma_to_host1x(cdma); |
106 | struct host1x_channel *ch = cdma_to_channel(cdma); | 103 | struct host1x_channel *ch = cdma_to_channel(cdma); |
104 | u64 start, end; | ||
107 | 105 | ||
108 | if (cdma->running) | 106 | if (cdma->running) |
109 | return; | 107 | return; |
@@ -113,10 +111,18 @@ static void cdma_timeout_restart(struct host1x_cdma *cdma, u32 getptr) | |||
113 | host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP, | 111 | host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP, |
114 | HOST1X_CHANNEL_DMACTRL); | 112 | HOST1X_CHANNEL_DMACTRL); |
115 | 113 | ||
114 | start = cdma->push_buffer.dma; | ||
115 | end = cdma->push_buffer.size + 4; | ||
116 | |||
116 | /* set base, end pointer (all of memory) */ | 117 | /* set base, end pointer (all of memory) */ |
117 | host1x_ch_writel(ch, cdma->push_buffer.dma, HOST1X_CHANNEL_DMASTART); | 118 | host1x_ch_writel(ch, lower_32_bits(start), HOST1X_CHANNEL_DMASTART); |
118 | host1x_ch_writel(ch, cdma->push_buffer.dma + cdma->push_buffer.size, | 119 | #if HOST1X_HW >= 6 |
119 | HOST1X_CHANNEL_DMAEND); | 120 | host1x_ch_writel(ch, upper_32_bits(start), HOST1X_CHANNEL_DMASTART_HI); |
121 | #endif | ||
122 | host1x_ch_writel(ch, lower_32_bits(end), HOST1X_CHANNEL_DMAEND); | ||
123 | #if HOST1X_HW >= 6 | ||
124 | host1x_ch_writel(ch, upper_32_bits(end), HOST1X_CHANNEL_DMAEND_HI); | ||
125 | #endif | ||
120 | 126 | ||
121 | /* set GET, by loading the value in PUT (then reset GET) */ | 127 | /* set GET, by loading the value in PUT (then reset GET) */ |
122 | host1x_ch_writel(ch, getptr, HOST1X_CHANNEL_DMAPUT); | 128 | host1x_ch_writel(ch, getptr, HOST1X_CHANNEL_DMAPUT); |
diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c index 95ea81172a83..27101c04a827 100644 --- a/drivers/gpu/host1x/hw/channel_hw.c +++ b/drivers/gpu/host1x/hw/channel_hw.c | |||
@@ -17,6 +17,7 @@ | |||
17 | */ | 17 | */ |
18 | 18 | ||
19 | #include <linux/host1x.h> | 19 | #include <linux/host1x.h> |
20 | #include <linux/iommu.h> | ||
20 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
21 | 22 | ||
22 | #include <trace/events/host1x.h> | 23 | #include <trace/events/host1x.h> |
@@ -60,15 +61,37 @@ static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo, | |||
60 | static void submit_gathers(struct host1x_job *job) | 61 | static void submit_gathers(struct host1x_job *job) |
61 | { | 62 | { |
62 | struct host1x_cdma *cdma = &job->channel->cdma; | 63 | struct host1x_cdma *cdma = &job->channel->cdma; |
64 | #if HOST1X_HW < 6 | ||
65 | struct device *dev = job->channel->dev; | ||
66 | #endif | ||
63 | unsigned int i; | 67 | unsigned int i; |
64 | 68 | ||
65 | for (i = 0; i < job->num_gathers; i++) { | 69 | for (i = 0; i < job->num_gathers; i++) { |
66 | struct host1x_job_gather *g = &job->gathers[i]; | 70 | struct host1x_job_gather *g = &job->gathers[i]; |
67 | u32 op1 = host1x_opcode_gather(g->words); | 71 | dma_addr_t addr = g->base + g->offset; |
68 | u32 op2 = g->base + g->offset; | 72 | u32 op2, op3; |
73 | |||
74 | op2 = lower_32_bits(addr); | ||
75 | op3 = upper_32_bits(addr); | ||
76 | |||
77 | trace_write_gather(cdma, g->bo, g->offset, g->words); | ||
78 | |||
79 | if (op3 != 0) { | ||
80 | #if HOST1X_HW >= 6 | ||
81 | u32 op1 = host1x_opcode_gather_wide(g->words); | ||
82 | u32 op4 = HOST1X_OPCODE_NOP; | ||
83 | |||
84 | host1x_cdma_push_wide(cdma, op1, op2, op3, op4); | ||
85 | #else | ||
86 | dev_err(dev, "invalid gather for push buffer %pad\n", | ||
87 | &addr); | ||
88 | continue; | ||
89 | #endif | ||
90 | } else { | ||
91 | u32 op1 = host1x_opcode_gather(g->words); | ||
69 | 92 | ||
70 | trace_write_gather(cdma, g->bo, g->offset, op1 & 0xffff); | 93 | host1x_cdma_push(cdma, op1, op2); |
71 | host1x_cdma_push(cdma, op1, op2); | 94 | } |
72 | } | 95 | } |
73 | } | 96 | } |
74 | 97 | ||
@@ -89,6 +112,16 @@ static inline void synchronize_syncpt_base(struct host1x_job *job) | |||
89 | HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(value)); | 112 | HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(value)); |
90 | } | 113 | } |
91 | 114 | ||
115 | static void host1x_channel_set_streamid(struct host1x_channel *channel) | ||
116 | { | ||
117 | #if HOST1X_HW >= 6 | ||
118 | struct iommu_fwspec *spec = dev_iommu_fwspec_get(channel->dev->parent); | ||
119 | u32 sid = spec ? spec->ids[0] & 0xffff : 0x7f; | ||
120 | |||
121 | host1x_ch_writel(channel, sid, HOST1X_CHANNEL_SMMU_STREAMID); | ||
122 | #endif | ||
123 | } | ||
124 | |||
92 | static int channel_submit(struct host1x_job *job) | 125 | static int channel_submit(struct host1x_job *job) |
93 | { | 126 | { |
94 | struct host1x_channel *ch = job->channel; | 127 | struct host1x_channel *ch = job->channel; |
@@ -120,6 +153,8 @@ static int channel_submit(struct host1x_job *job) | |||
120 | goto error; | 153 | goto error; |
121 | } | 154 | } |
122 | 155 | ||
156 | host1x_channel_set_streamid(ch); | ||
157 | |||
123 | /* begin a CDMA submit */ | 158 | /* begin a CDMA submit */ |
124 | err = host1x_cdma_begin(&ch->cdma, job); | 159 | err = host1x_cdma_begin(&ch->cdma, job); |
125 | if (err) { | 160 | if (err) { |
diff --git a/drivers/gpu/host1x/hw/host1x06_hardware.h b/drivers/gpu/host1x/hw/host1x06_hardware.h index 3039c92ea605..dd37b10c8d04 100644 --- a/drivers/gpu/host1x/hw/host1x06_hardware.h +++ b/drivers/gpu/host1x/hw/host1x06_hardware.h | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/types.h> | 22 | #include <linux/types.h> |
23 | #include <linux/bitops.h> | 23 | #include <linux/bitops.h> |
24 | 24 | ||
25 | #include "hw_host1x06_channel.h" | ||
25 | #include "hw_host1x06_uclass.h" | 26 | #include "hw_host1x06_uclass.h" |
26 | #include "hw_host1x06_vm.h" | 27 | #include "hw_host1x06_vm.h" |
27 | #include "hw_host1x06_hypervisor.h" | 28 | #include "hw_host1x06_hypervisor.h" |
@@ -137,6 +138,11 @@ static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count) | |||
137 | return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count; | 138 | return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count; |
138 | } | 139 | } |
139 | 140 | ||
141 | static inline u32 host1x_opcode_gather_wide(unsigned count) | ||
142 | { | ||
143 | return (12 << 28) | count; | ||
144 | } | ||
145 | |||
140 | #define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0) | 146 | #define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0) |
141 | 147 | ||
142 | #endif | 148 | #endif |
diff --git a/drivers/gpu/host1x/hw/host1x07_hardware.h b/drivers/gpu/host1x/hw/host1x07_hardware.h index 1353e7ab71dd..9f6da4ee5443 100644 --- a/drivers/gpu/host1x/hw/host1x07_hardware.h +++ b/drivers/gpu/host1x/hw/host1x07_hardware.h | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/types.h> | 22 | #include <linux/types.h> |
23 | #include <linux/bitops.h> | 23 | #include <linux/bitops.h> |
24 | 24 | ||
25 | #include "hw_host1x07_channel.h" | ||
25 | #include "hw_host1x07_uclass.h" | 26 | #include "hw_host1x07_uclass.h" |
26 | #include "hw_host1x07_vm.h" | 27 | #include "hw_host1x07_vm.h" |
27 | #include "hw_host1x07_hypervisor.h" | 28 | #include "hw_host1x07_hypervisor.h" |
@@ -137,6 +138,11 @@ static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count) | |||
137 | return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count; | 138 | return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count; |
138 | } | 139 | } |
139 | 140 | ||
141 | static inline u32 host1x_opcode_gather_wide(unsigned count) | ||
142 | { | ||
143 | return (12 << 28) | count; | ||
144 | } | ||
145 | |||
140 | #define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0) | 146 | #define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0) |
141 | 147 | ||
142 | #endif | 148 | #endif |
diff --git a/drivers/gpu/host1x/hw/hw_host1x06_channel.h b/drivers/gpu/host1x/hw/hw_host1x06_channel.h new file mode 100644 index 000000000000..18ae1c57bbea --- /dev/null +++ b/drivers/gpu/host1x/hw/hw_host1x06_channel.h | |||
@@ -0,0 +1,11 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (C) 2019 NVIDIA Corporation. | ||
4 | */ | ||
5 | |||
6 | #ifndef HOST1X_HW_HOST1X06_CHANNEL_H | ||
7 | #define HOST1X_HW_HOST1X06_CHANNEL_H | ||
8 | |||
9 | #define HOST1X_CHANNEL_SMMU_STREAMID 0x084 | ||
10 | |||
11 | #endif | ||
diff --git a/drivers/gpu/host1x/hw/hw_host1x07_channel.h b/drivers/gpu/host1x/hw/hw_host1x07_channel.h new file mode 100644 index 000000000000..96fa72bbd7ab --- /dev/null +++ b/drivers/gpu/host1x/hw/hw_host1x07_channel.h | |||
@@ -0,0 +1,11 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (C) 2019 NVIDIA Corporation. | ||
4 | */ | ||
5 | |||
6 | #ifndef HOST1X_HW_HOST1X07_CHANNEL_H | ||
7 | #define HOST1X_HW_HOST1X07_CHANNEL_H | ||
8 | |||
9 | #define HOST1X_CHANNEL_SMMU_STREAMID 0x084 | ||
10 | |||
11 | #endif | ||