Merge remote-tracking branch 'drm-tegra/drm/tegra/for-next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Fri, 8 Feb 2019 01:03:23 +0000 (12:03 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Fri, 8 Feb 2019 01:03:23 +0000 (12:03 +1100)
23 files changed:
Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt
drivers/gpu/drm/tegra/Makefile
drivers/gpu/drm/tegra/drm.c
drivers/gpu/drm/tegra/drm.h
drivers/gpu/drm/tegra/hda.c [new file with mode: 0644]
drivers/gpu/drm/tegra/hda.h [new file with mode: 0644]
drivers/gpu/drm/tegra/hdmi.c
drivers/gpu/drm/tegra/output.c
drivers/gpu/drm/tegra/sor.c
drivers/gpu/drm/tegra/vic.c
drivers/gpu/drm/tegra/vic.h
drivers/gpu/host1x/bus.c
drivers/gpu/host1x/cdma.c
drivers/gpu/host1x/cdma.h
drivers/gpu/host1x/dev.c
drivers/gpu/host1x/dev.h
drivers/gpu/host1x/hw/cdma_hw.c
drivers/gpu/host1x/hw/channel_hw.c
drivers/gpu/host1x/hw/host1x06_hardware.h
drivers/gpu/host1x/hw/host1x07_hardware.h
drivers/gpu/host1x/hw/hw_host1x06_channel.h [new file with mode: 0644]
drivers/gpu/host1x/hw/hw_host1x07_channel.h [new file with mode: 0644]
include/trace/events/host1x.h

index 593be44..9999255 100644 (file)
@@ -238,6 +238,9 @@ of the following host1x client modules:
   - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection
   - nvidia,edid: supplies a binary EDID blob
   - nvidia,panel: phandle of a display panel
+  - nvidia,xbar-cfg: 5 cells containing the crossbar configuration. Each lane
+    of the SOR, identified by the cell's index, is mapped via the crossbar to
+    the pad specified by the cell's value.
 
   Optional properties when driving an eDP output:
   - nvidia,dpaux: phandle to a DispayPort AUX interface
index 2e0d621..33c463e 100644 (file)
@@ -10,6 +10,7 @@ tegra-drm-y := \
        dc.o \
        output.o \
        rgb.o \
+       hda.o \
        hdmi.o \
        mipi-phy.o \
        dsi.o \
index 4b70ce6..0c5f1e6 100644 (file)
@@ -92,10 +92,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
                return -ENOMEM;
 
        if (iommu_present(&platform_bus_type)) {
-               u64 carveout_start, carveout_end, gem_start, gem_end;
-               struct iommu_domain_geometry *geometry;
-               unsigned long order;
-
                tegra->domain = iommu_domain_alloc(&platform_bus_type);
                if (!tegra->domain) {
                        err = -ENOMEM;
@@ -105,27 +101,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
                err = iova_cache_get();
                if (err < 0)
                        goto domain;
-
-               geometry = &tegra->domain->geometry;
-               gem_start = geometry->aperture_start;
-               gem_end = geometry->aperture_end - CARVEOUT_SZ;
-               carveout_start = gem_end + 1;
-               carveout_end = geometry->aperture_end;
-
-               order = __ffs(tegra->domain->pgsize_bitmap);
-               init_iova_domain(&tegra->carveout.domain, 1UL << order,
-                                carveout_start >> order);
-
-               tegra->carveout.shift = iova_shift(&tegra->carveout.domain);
-               tegra->carveout.limit = carveout_end >> tegra->carveout.shift;
-
-               drm_mm_init(&tegra->mm, gem_start, gem_end - gem_start + 1);
-               mutex_init(&tegra->mm_lock);
-
-               DRM_DEBUG("IOMMU apertures:\n");
-               DRM_DEBUG("  GEM: %#llx-%#llx\n", gem_start, gem_end);
-               DRM_DEBUG("  Carveout: %#llx-%#llx\n", carveout_start,
-                         carveout_end);
        }
 
        mutex_init(&tegra->clients_lock);
@@ -159,6 +134,36 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
        if (err < 0)
                goto fbdev;
 
+       if (tegra->domain) {
+               u64 carveout_start, carveout_end, gem_start, gem_end;
+               u64 dma_mask = dma_get_mask(&device->dev);
+               dma_addr_t start, end;
+               unsigned long order;
+
+               start = tegra->domain->geometry.aperture_start & dma_mask;
+               end = tegra->domain->geometry.aperture_end & dma_mask;
+
+               gem_start = start;
+               gem_end = end - CARVEOUT_SZ;
+               carveout_start = gem_end + 1;
+               carveout_end = end;
+
+               order = __ffs(tegra->domain->pgsize_bitmap);
+               init_iova_domain(&tegra->carveout.domain, 1UL << order,
+                                carveout_start >> order);
+
+               tegra->carveout.shift = iova_shift(&tegra->carveout.domain);
+               tegra->carveout.limit = carveout_end >> tegra->carveout.shift;
+
+               drm_mm_init(&tegra->mm, gem_start, gem_end - gem_start + 1);
+               mutex_init(&tegra->mm_lock);
+
+               DRM_DEBUG("IOMMU apertures:\n");
+               DRM_DEBUG("  GEM: %#llx-%#llx\n", gem_start, gem_end);
+               DRM_DEBUG("  Carveout: %#llx-%#llx\n", carveout_start,
+                         carveout_end);
+       }
+
        if (tegra->hub) {
                err = tegra_display_hub_prepare(tegra->hub);
                if (err < 0)
@@ -1041,6 +1046,7 @@ int tegra_drm_register_client(struct tegra_drm *tegra,
 {
        mutex_lock(&tegra->clients_lock);
        list_add_tail(&client->list, &tegra->clients);
+       client->drm = tegra;
        mutex_unlock(&tegra->clients_lock);
 
        return 0;
@@ -1051,6 +1057,7 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra,
 {
        mutex_lock(&tegra->clients_lock);
        list_del_init(&client->list);
+       client->drm = NULL;
        mutex_unlock(&tegra->clients_lock);
 
        return 0;
index e2dee5c..70154c2 100644 (file)
@@ -88,6 +88,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,
 struct tegra_drm_client {
        struct host1x_client base;
        struct list_head list;
+       struct tegra_drm *drm;
 
        unsigned int version;
        const struct tegra_drm_client_ops *ops;
@@ -124,7 +125,7 @@ struct tegra_output {
        struct drm_panel *panel;
        struct i2c_adapter *ddc;
        const struct edid *edid;
-       struct cec_notifier *notifier;
+       struct cec_notifier *cec;
        unsigned int hpd_irq;
        int hpd_gpio;
        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 (file)
index 0000000..94245a1
--- /dev/null
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2019 NVIDIA Corporation
+ */
+
+#include <linux/bug.h>
+
+#include <sound/hda_verbs.h>
+
+#include "hda.h"
+
+void tegra_hda_parse_format(unsigned int format, struct tegra_hda_format *fmt)
+{
+       unsigned int mul, div, bits, channels;
+
+       if (format & AC_FMT_TYPE_NON_PCM)
+               fmt->pcm = false;
+       else
+               fmt->pcm = true;
+
+       if (format & AC_FMT_BASE_44K)
+               fmt->sample_rate = 44100;
+       else
+               fmt->sample_rate = 48000;
+
+       mul = (format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT;
+       div = (format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT;
+
+       fmt->sample_rate *= (mul + 1) / (div + 1);
+
+       switch (format & AC_FMT_BITS_MASK) {
+       case AC_FMT_BITS_8:
+               fmt->bits = 8;
+               break;
+
+       case AC_FMT_BITS_16:
+               fmt->bits = 16;
+               break;
+
+       case AC_FMT_BITS_20:
+               fmt->bits = 20;
+               break;
+
+       case AC_FMT_BITS_24:
+               fmt->bits = 24;
+               break;
+
+       case AC_FMT_BITS_32:
+               fmt->bits = 32;
+               break;
+
+       default:
+               bits = (format & AC_FMT_BITS_MASK) >> AC_FMT_BITS_SHIFT;
+               WARN(1, "invalid number of bits: %#x\n", bits);
+               fmt->bits = 8;
+               break;
+       }
+
+       channels = (format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT;
+
+       /* channels are encoded as n - 1 */
+       fmt->channels = channels + 1;
+}
diff --git a/drivers/gpu/drm/tegra/hda.h b/drivers/gpu/drm/tegra/hda.h
new file mode 100644 (file)
index 0000000..7726995
--- /dev/null
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2019 NVIDIA Corporation
+ */
+
+#ifndef DRM_TEGRA_HDA_H
+#define DRM_TEGRA_HDA_H 1
+
+#include <linux/types.h>
+
+struct tegra_hda_format {
+       unsigned int sample_rate;
+       unsigned int channels;
+       unsigned int bits;
+       bool pcm;
+};
+
+void tegra_hda_parse_format(unsigned int format, struct tegra_hda_format *fmt);
+
+#endif
index 78bc7c9..47c5597 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/debugfs.h>
 #include <linux/gpio.h>
 #include <linux/hdmi.h>
+#include <linux/math64.h>
 #include <linux/of_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_probe_helper.h>
 
-#include <sound/hda_verbs.h>
-
-#include <media/cec-notifier.h>
-
+#include "hda.h"
 #include "hdmi.h"
 #include "drm.h"
 #include "dc.h"
@@ -71,8 +69,7 @@ struct tegra_hdmi {
        const struct tegra_hdmi_config *config;
 
        unsigned int audio_source;
-       unsigned int audio_sample_rate;
-       unsigned int audio_channels;
+       struct tegra_hda_format format;
 
        unsigned int pixel_clock;
        bool stereo;
@@ -119,68 +116,11 @@ static inline void tegra_hdmi_writel(struct tegra_hdmi *hdmi, u32 value,
 }
 
 struct tegra_hdmi_audio_config {
-       unsigned int pclk;
        unsigned int n;
        unsigned int cts;
        unsigned int aval;
 };
 
-static const struct tegra_hdmi_audio_config tegra_hdmi_audio_32k[] = {
-       {  25200000, 4096,  25200, 24000 },
-       {  27000000, 4096,  27000, 24000 },
-       {  74250000, 4096,  74250, 24000 },
-       { 148500000, 4096, 148500, 24000 },
-       {         0,    0,      0,     0 },
-};
-
-static const struct tegra_hdmi_audio_config tegra_hdmi_audio_44_1k[] = {
-       {  25200000, 5880,  26250, 25000 },
-       {  27000000, 5880,  28125, 25000 },
-       {  74250000, 4704,  61875, 20000 },
-       { 148500000, 4704, 123750, 20000 },
-       {         0,    0,      0,     0 },
-};
-
-static const struct tegra_hdmi_audio_config tegra_hdmi_audio_48k[] = {
-       {  25200000, 6144,  25200, 24000 },
-       {  27000000, 6144,  27000, 24000 },
-       {  74250000, 6144,  74250, 24000 },
-       { 148500000, 6144, 148500, 24000 },
-       {         0,    0,      0,     0 },
-};
-
-static const struct tegra_hdmi_audio_config tegra_hdmi_audio_88_2k[] = {
-       {  25200000, 11760,  26250, 25000 },
-       {  27000000, 11760,  28125, 25000 },
-       {  74250000,  9408,  61875, 20000 },
-       { 148500000,  9408, 123750, 20000 },
-       {         0,     0,      0,     0 },
-};
-
-static const struct tegra_hdmi_audio_config tegra_hdmi_audio_96k[] = {
-       {  25200000, 12288,  25200, 24000 },
-       {  27000000, 12288,  27000, 24000 },
-       {  74250000, 12288,  74250, 24000 },
-       { 148500000, 12288, 148500, 24000 },
-       {         0,     0,      0,     0 },
-};
-
-static const struct tegra_hdmi_audio_config tegra_hdmi_audio_176_4k[] = {
-       {  25200000, 23520,  26250, 25000 },
-       {  27000000, 23520,  28125, 25000 },
-       {  74250000, 18816,  61875, 20000 },
-       { 148500000, 18816, 123750, 20000 },
-       {         0,     0,      0,     0 },
-};
-
-static const struct tegra_hdmi_audio_config tegra_hdmi_audio_192k[] = {
-       {  25200000, 24576,  25200, 24000 },
-       {  27000000, 24576,  27000, 24000 },
-       {  74250000, 24576,  74250, 24000 },
-       { 148500000, 24576, 148500, 24000 },
-       {         0,     0,      0,     0 },
-};
-
 static const struct tmds_config tegra20_tmds_config[] = {
        { /* slow pixel clock modes */
                .pclk = 27000000,
@@ -418,52 +358,53 @@ static const struct tmds_config tegra124_tmds_config[] = {
        },
 };
 
-static const struct tegra_hdmi_audio_config *
-tegra_hdmi_get_audio_config(unsigned int sample_rate, unsigned int pclk)
+static int
+tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pix_clock,
+                           struct tegra_hdmi_audio_config *config)
 {
-       const struct tegra_hdmi_audio_config *table;
-
-       switch (sample_rate) {
-       case 32000:
-               table = tegra_hdmi_audio_32k;
-               break;
-
-       case 44100:
-               table = tegra_hdmi_audio_44_1k;
-               break;
-
-       case 48000:
-               table = tegra_hdmi_audio_48k;
-               break;
-
-       case 88200:
-               table = tegra_hdmi_audio_88_2k;
-               break;
-
-       case 96000:
-               table = tegra_hdmi_audio_96k;
-               break;
-
-       case 176400:
-               table = tegra_hdmi_audio_176_4k;
-               break;
-
-       case 192000:
-               table = tegra_hdmi_audio_192k;
-               break;
-
-       default:
-               return NULL;
-       }
-
-       while (table->pclk) {
-               if (table->pclk == pclk)
-                       return table;
-
-               table++;
+       const unsigned int afreq = 128 * audio_freq;
+       const unsigned int min_n = afreq / 1500;
+       const unsigned int max_n = afreq / 300;
+       const unsigned int ideal_n = afreq / 1000;
+       int64_t min_err = (uint64_t)-1 >> 1;
+       unsigned int min_delta = -1;
+       int n;
+
+       memset(config, 0, sizeof(*config));
+       config->n = -1;
+
+       for (n = min_n; n <= max_n; n++) {
+               uint64_t cts_f, aval_f;
+               unsigned int delta;
+               int64_t cts, err;
+
+               /* compute aval in 48.16 fixed point */
+               aval_f = ((int64_t)24000000 << 16) * n;
+               do_div(aval_f, afreq);
+               /* It should round without any rest */
+               if (aval_f & 0xFFFF)
+                       continue;
+
+               /* Compute cts in 48.16 fixed point */
+               cts_f = ((int64_t)pix_clock << 16) * n;
+               do_div(cts_f, afreq);
+               /* Round it to the nearest integer */
+               cts = (cts_f & ~0xFFFF) + ((cts_f & BIT(15)) << 1);
+
+               delta = abs(n - ideal_n);
+
+               /* Compute the absolute error */
+               err = abs((int64_t)cts_f - cts);
+               if (err < min_err || (err == min_err && delta < min_delta)) {
+                       config->n = n;
+                       config->cts = cts >> 16;
+                       config->aval = aval_f >> 16;
+                       min_delta = delta;
+                       min_err = err;
+               }
        }
 
-       return NULL;
+       return config->n != -1 ? 0 : -EINVAL;
 }
 
 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)
        unsigned int i;
 
        for (i = 0; i < ARRAY_SIZE(regs); i++) {
-               if (regs[i].sample_rate == hdmi->audio_sample_rate) {
+               if (regs[i].sample_rate == hdmi->format.sample_rate) {
                        tegra_hdmi_writel(hdmi, value, regs[i].offset);
                        break;
                }
@@ -519,8 +460,9 @@ static void tegra_hdmi_write_aval(struct tegra_hdmi *hdmi, u32 value)
 
 static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi)
 {
-       const struct tegra_hdmi_audio_config *config;
+       struct tegra_hdmi_audio_config config;
        u32 source, value;
+       int err;
 
        switch (hdmi->audio_source) {
        case HDA:
@@ -564,7 +506,7 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi)
                 * play back system startup sounds early. It is possibly not
                 * needed on Linux at all.
                 */
-               if (hdmi->audio_channels == 2)
+               if (hdmi->format.channels == 2)
                        value = SOR_AUDIO_CNTRL0_INJECT_NULLSMPL;
                else
                        value = 0;
@@ -595,25 +537,28 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi)
                tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_SPARE0);
        }
 
-       config = tegra_hdmi_get_audio_config(hdmi->audio_sample_rate,
-                                            hdmi->pixel_clock);
-       if (!config) {
+       err = tegra_hdmi_get_audio_config(hdmi->format.sample_rate,
+                                         hdmi->pixel_clock, &config);
+       if (err < 0) {
                dev_err(hdmi->dev,
                        "cannot set audio to %u Hz at %u Hz pixel clock\n",
-                       hdmi->audio_sample_rate, hdmi->pixel_clock);
-               return -EINVAL;
+                       hdmi->format.sample_rate, hdmi->pixel_clock);
+               return err;
        }
 
+       dev_dbg(hdmi->dev, "audio: pixclk=%u, n=%u, cts=%u, aval=%u\n",
+               hdmi->pixel_clock, config.n, config.cts, config.aval);
+
        tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_HDMI_ACR_CTRL);
 
        value = AUDIO_N_RESETF | AUDIO_N_GENERATE_ALTERNATE |
-               AUDIO_N_VALUE(config->n - 1);
+               AUDIO_N_VALUE(config.n - 1);
        tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N);
 
-       tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config->n) | ACR_ENABLE,
+       tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config.n) | ACR_ENABLE,
                          HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH);
 
-       tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config->cts),
+       tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config.cts),
                          HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW);
 
        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)
        tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N);
 
        if (hdmi->config->has_hda)
-               tegra_hdmi_write_aval(hdmi, config->aval);
+               tegra_hdmi_write_aval(hdmi, config.aval);
 
        tegra_hdmi_setup_audio_fs_tables(hdmi);
 
@@ -788,7 +733,7 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
                return;
        }
 
-       frame.channels = hdmi->audio_channels;
+       frame.channels = hdmi->format.channels;
 
        err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
        if (err < 0) {
@@ -1590,24 +1535,6 @@ static const struct of_device_id tegra_hdmi_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, tegra_hdmi_of_match);
 
-static void hda_format_parse(unsigned int format, unsigned int *rate,
-                            unsigned int *channels)
-{
-       unsigned int mul, div;
-
-       if (format & AC_FMT_BASE_44K)
-               *rate = 44100;
-       else
-               *rate = 48000;
-
-       mul = (format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT;
-       div = (format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT;
-
-       *rate = *rate * (mul + 1) / (div + 1);
-
-       *channels = (format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT;
-}
-
 static irqreturn_t tegra_hdmi_irq(int irq, void *data)
 {
        struct tegra_hdmi *hdmi = data;
@@ -1624,14 +1551,9 @@ static irqreturn_t tegra_hdmi_irq(int irq, void *data)
                value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0);
 
                if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) {
-                       unsigned int sample_rate, channels;
-
                        format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK;
 
-                       hda_format_parse(format, &sample_rate, &channels);
-
-                       hdmi->audio_sample_rate = sample_rate;
-                       hdmi->audio_channels = channels;
+                       tegra_hda_parse_format(format, &hdmi->format);
 
                        err = tegra_hdmi_setup_audio(hdmi);
                        if (err < 0) {
@@ -1665,8 +1587,6 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
        hdmi->dev = &pdev->dev;
 
        hdmi->audio_source = AUTO;
-       hdmi->audio_sample_rate = 48000;
-       hdmi->audio_channels = 2;
        hdmi->stereo = false;
        hdmi->dvi = false;
 
@@ -1710,10 +1630,6 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
                return PTR_ERR(hdmi->vdd);
        }
 
-       hdmi->output.notifier = cec_notifier_get(&pdev->dev);
-       if (hdmi->output.notifier == NULL)
-               return -ENOMEM;
-
        hdmi->output.dev = &pdev->dev;
 
        err = tegra_output_probe(&hdmi->output);
@@ -1772,9 +1688,6 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
 
        tegra_output_remove(&hdmi->output);
 
-       if (hdmi->output.notifier)
-               cec_notifier_put(hdmi->output.notifier);
-
        return 0;
 }
 
index c662efc..9c2b9da 100644 (file)
@@ -36,7 +36,7 @@ int tegra_output_connector_get_modes(struct drm_connector *connector)
        else if (output->ddc)
                edid = drm_get_edid(connector, output->ddc);
 
-       cec_notifier_set_phys_addr_from_edid(output->notifier, edid);
+       cec_notifier_set_phys_addr_from_edid(output->cec, edid);
        drm_connector_update_edid_property(connector, edid);
 
        if (edid) {
@@ -73,7 +73,7 @@ tegra_output_connector_detect(struct drm_connector *connector, bool force)
        }
 
        if (status != connector_status_connected)
-               cec_notifier_phys_addr_invalidate(output->notifier);
+               cec_notifier_phys_addr_invalidate(output->cec);
 
        return status;
 }
@@ -174,11 +174,18 @@ int tegra_output_probe(struct tegra_output *output)
                disable_irq(output->hpd_irq);
        }
 
+       output->cec = cec_notifier_get(output->dev);
+       if (!output->cec)
+               return -ENOMEM;
+
        return 0;
 }
 
 void tegra_output_remove(struct tegra_output *output)
 {
+       if (output->cec)
+               cec_notifier_put(output->cec);
+
        if (gpio_is_valid(output->hpd_gpio)) {
                free_irq(output->hpd_irq, output);
                gpio_free(output->hpd_gpio);
index 44feac2..4005710 100644 (file)
@@ -19,8 +19,6 @@
 
 #include <soc/tegra/pmc.h>
 
-#include <sound/hda_verbs.h>
-
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_panel.h>
@@ -28,6 +26,7 @@
 
 #include "dc.h"
 #include "drm.h"
+#include "hda.h"
 #include "sor.h"
 #include "trace.h"
 
@@ -411,6 +410,8 @@ struct tegra_sor {
        struct clk *clk_dp;
        struct clk *clk;
 
+       u8 xbar_cfg[5];
+
        struct drm_dp_aux *aux;
 
        struct drm_info_list *debugfs_files;
@@ -429,10 +430,7 @@ struct tegra_sor {
        struct delayed_work scdc;
        bool scdc_enabled;
 
-       struct {
-               unsigned int sample_rate;
-               unsigned int channels;
-       } audio;
+       struct tegra_hda_format format;
 };
 
 struct tegra_sor_state {
@@ -1818,7 +1816,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
 
        /* XXX not in TRM */
        for (value = 0, i = 0; i < 5; i++)
-               value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->soc->xbar_cfg[i]) |
+               value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->xbar_cfg[i]) |
                         SOR_XBAR_CTRL_LINK1_XSEL(i, i);
 
        tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL);
@@ -2186,7 +2184,7 @@ static int tegra_sor_hdmi_enable_audio_infoframe(struct tegra_sor *sor)
                return err;
        }
 
-       frame.channels = sor->audio.channels;
+       frame.channels = sor->format.channels;
 
        err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
        if (err < 0) {
@@ -2215,7 +2213,7 @@ static void tegra_sor_hdmi_audio_enable(struct tegra_sor *sor)
        value |= SOR_AUDIO_CNTRL_SOURCE_SELECT(SOURCE_SELECT_HDA);
 
        /* inject null samples */
-       if (sor->audio.channels != 2)
+       if (sor->format.channels != 2)
                value &= ~SOR_AUDIO_CNTRL_INJECT_NULLSMPL;
        else
                value |= SOR_AUDIO_CNTRL_INJECT_NULLSMPL;
@@ -2246,7 +2244,7 @@ static void tegra_sor_hdmi_audio_enable(struct tegra_sor *sor)
        value = SOR_HDMI_AUDIO_N_RESET | SOR_HDMI_AUDIO_N_LOOKUP;
        tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_N);
 
-       value = (24000 * 4096) / (128 * sor->audio.sample_rate / 1000);
+       value = (24000 * 4096) / (128 * sor->format.sample_rate / 1000);
        tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0320);
        tegra_sor_writel(sor, 4096, SOR_AUDIO_NVAL_0320);
 
@@ -2259,15 +2257,15 @@ static void tegra_sor_hdmi_audio_enable(struct tegra_sor *sor)
        tegra_sor_writel(sor, 20000, SOR_AUDIO_AVAL_1764);
        tegra_sor_writel(sor, 18816, SOR_AUDIO_NVAL_1764);
 
-       value = (24000 * 6144) / (128 * sor->audio.sample_rate / 1000);
+       value = (24000 * 6144) / (128 * sor->format.sample_rate / 1000);
        tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0480);
        tegra_sor_writel(sor, 6144, SOR_AUDIO_NVAL_0480);
 
-       value = (24000 * 12288) / (128 * sor->audio.sample_rate / 1000);
+       value = (24000 * 12288) / (128 * sor->format.sample_rate / 1000);
        tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0960);
        tegra_sor_writel(sor, 12288, SOR_AUDIO_NVAL_0960);
 
-       value = (24000 * 24576) / (128 * sor->audio.sample_rate / 1000);
+       value = (24000 * 24576) / (128 * sor->format.sample_rate / 1000);
        tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_1920);
        tegra_sor_writel(sor, 24576, SOR_AUDIO_NVAL_1920);
 
@@ -2555,7 +2553,7 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
 
        /* XXX not in TRM */
        for (value = 0, i = 0; i < 5; i++)
-               value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->soc->xbar_cfg[i]) |
+               value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->xbar_cfg[i]) |
                         SOR_XBAR_CTRL_LINK1_XSEL(i, i);
 
        tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL);
@@ -3176,6 +3174,8 @@ MODULE_DEVICE_TABLE(of, tegra_sor_of_match);
 static int tegra_sor_parse_dt(struct tegra_sor *sor)
 {
        struct device_node *np = sor->dev->of_node;
+       u32 xbar_cfg[5];
+       unsigned int i;
        u32 value;
        int err;
 
@@ -3193,25 +3193,18 @@ static int tegra_sor_parse_dt(struct tegra_sor *sor)
                sor->pad = TEGRA_IO_PAD_HDMI_DP0 + sor->index;
        }
 
-       return 0;
-}
-
-static void tegra_hda_parse_format(unsigned int format, unsigned int *rate,
-                                  unsigned int *channels)
-{
-       unsigned int mul, div;
-
-       if (format & AC_FMT_BASE_44K)
-               *rate = 44100;
-       else
-               *rate = 48000;
-
-       mul = (format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT;
-       div = (format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT;
-
-       *rate = *rate * (mul + 1) / (div + 1);
+       err = of_property_read_u32_array(np, "nvidia,xbar-cfg", xbar_cfg, 5);
+       if (err < 0) {
+               /* fall back to default per-SoC XBAR configuration */
+               for (i = 0; i < 5; i++)
+                       sor->xbar_cfg[i] = sor->soc->xbar_cfg[i];
+       } else {
+               /* copy cells to SOR XBAR configuration */
+               for (i = 0; i < 5; i++)
+                       sor->xbar_cfg[i] = xbar_cfg[i];
+       }
 
-       *channels = (format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT;
+       return 0;
 }
 
 static irqreturn_t tegra_sor_irq(int irq, void *data)
@@ -3226,14 +3219,11 @@ static irqreturn_t tegra_sor_irq(int irq, void *data)
                value = tegra_sor_readl(sor, SOR_AUDIO_HDA_CODEC_SCRATCH0);
 
                if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) {
-                       unsigned int format, sample_rate, channels;
+                       unsigned int format;
 
                        format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK;
 
-                       tegra_hda_parse_format(format, &sample_rate, &channels);
-
-                       sor->audio.sample_rate = sample_rate;
-                       sor->audio.channels = channels;
+                       tegra_hda_parse_format(format, &sor->format);
 
                        tegra_sor_hdmi_audio_enable(sor);
                } else {
index d47983d..39bfed9 100644 (file)
@@ -26,6 +26,7 @@
 struct vic_config {
        const char *firmware;
        unsigned int version;
+       bool supports_sid;
 };
 
 struct vic {
@@ -105,6 +106,22 @@ static int vic_boot(struct vic *vic)
        if (vic->booted)
                return 0;
 
+       if (vic->config->supports_sid) {
+               struct iommu_fwspec *spec = dev_iommu_fwspec_get(vic->dev);
+               u32 value;
+
+               value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) |
+                       TRANSCFG_ATT(0, TRANSCFG_SID_HW);
+               vic_writel(vic, value, VIC_TFBIF_TRANSCFG);
+
+               if (spec && spec->num_ids > 0) {
+                       value = spec->ids[0] & 0xffff;
+
+                       vic_writel(vic, value, VIC_THI_STREAMID0);
+                       vic_writel(vic, value, VIC_THI_STREAMID1);
+               }
+       }
+
        /* setup clockgating registers */
        vic_writel(vic, CG_IDLE_CG_DLY_CNT(4) |
                        CG_IDLE_CG_EN |
@@ -181,13 +198,6 @@ static int vic_init(struct host1x_client *client)
                vic->domain = tegra->domain;
        }
 
-       if (!vic->falcon.data) {
-               vic->falcon.data = tegra;
-               err = falcon_load_firmware(&vic->falcon);
-               if (err < 0)
-                       goto detach;
-       }
-
        vic->channel = host1x_channel_request(client->dev);
        if (!vic->channel) {
                err = -ENOMEM;
@@ -246,6 +256,30 @@ static const struct host1x_client_ops vic_client_ops = {
        .exit = vic_exit,
 };
 
+static int vic_load_firmware(struct vic *vic)
+{
+       int err;
+
+       if (vic->falcon.data)
+               return 0;
+
+       vic->falcon.data = vic->client.drm;
+
+       err = falcon_read_firmware(&vic->falcon, vic->config->firmware);
+       if (err < 0)
+               goto cleanup;
+
+       err = falcon_load_firmware(&vic->falcon);
+       if (err < 0)
+               goto cleanup;
+
+       return 0;
+
+cleanup:
+       vic->falcon.data = NULL;
+       return err;
+}
+
 static int vic_open_channel(struct tegra_drm_client *client,
                            struct tegra_drm_context *context)
 {
@@ -256,19 +290,25 @@ static int vic_open_channel(struct tegra_drm_client *client,
        if (err < 0)
                return err;
 
+       err = vic_load_firmware(vic);
+       if (err < 0)
+               goto rpm_put;
+
        err = vic_boot(vic);
-       if (err < 0) {
-               pm_runtime_put(vic->dev);
-               return err;
-       }
+       if (err < 0)
+               goto rpm_put;
 
        context->channel = host1x_channel_get(vic->channel);
        if (!context->channel) {
-               pm_runtime_put(vic->dev);
-               return -ENOMEM;
+               err = -ENOMEM;
+               goto rpm_put;
        }
 
        return 0;
+
+rpm_put:
+       pm_runtime_put(vic->dev);
+       return err;
 }
 
 static void vic_close_channel(struct tegra_drm_context *context)
@@ -291,6 +331,7 @@ static const struct tegra_drm_client_ops vic_ops = {
 static const struct vic_config vic_t124_config = {
        .firmware = NVIDIA_TEGRA_124_VIC_FIRMWARE,
        .version = 0x40,
+       .supports_sid = false,
 };
 
 #define NVIDIA_TEGRA_210_VIC_FIRMWARE "nvidia/tegra210/vic04_ucode.bin"
@@ -298,6 +339,7 @@ static const struct vic_config vic_t124_config = {
 static const struct vic_config vic_t210_config = {
        .firmware = NVIDIA_TEGRA_210_VIC_FIRMWARE,
        .version = 0x21,
+       .supports_sid = false,
 };
 
 #define NVIDIA_TEGRA_186_VIC_FIRMWARE "nvidia/tegra186/vic04_ucode.bin"
@@ -305,6 +347,7 @@ static const struct vic_config vic_t210_config = {
 static const struct vic_config vic_t186_config = {
        .firmware = NVIDIA_TEGRA_186_VIC_FIRMWARE,
        .version = 0x18,
+       .supports_sid = true,
 };
 
 #define NVIDIA_TEGRA_194_VIC_FIRMWARE "nvidia/tegra194/vic.bin"
@@ -312,6 +355,7 @@ static const struct vic_config vic_t186_config = {
 static const struct vic_config vic_t194_config = {
        .firmware = NVIDIA_TEGRA_194_VIC_FIRMWARE,
        .version = 0x19,
+       .supports_sid = true,
 };
 
 static const struct of_device_id vic_match[] = {
@@ -372,10 +416,6 @@ static int vic_probe(struct platform_device *pdev)
        if (err < 0)
                return err;
 
-       err = falcon_read_firmware(&vic->falcon, vic->config->firmware);
-       if (err < 0)
-               goto exit_falcon;
-
        platform_set_drvdata(pdev, vic);
 
        INIT_LIST_HEAD(&vic->client.base.list);
@@ -393,7 +433,6 @@ static int vic_probe(struct platform_device *pdev)
        err = host1x_client_register(&vic->client.base);
        if (err < 0) {
                dev_err(dev, "failed to register host1x client: %d\n", err);
-               platform_set_drvdata(pdev, NULL);
                goto exit_falcon;
        }
 
index 2184481..0175843 100644 (file)
 
 /* VIC registers */
 
+#define VIC_THI_STREAMID0      0x00000030
+#define VIC_THI_STREAMID1      0x00000034
+
 #define NV_PVIC_MISC_PRI_VIC_CG                        0x000016d0
 #define CG_IDLE_CG_DLY_CNT(val)                        ((val & 0x3f) << 0)
 #define CG_IDLE_CG_EN                          (1 << 6)
 #define CG_WAKEUP_DLY_CNT(val)                 ((val & 0xf) << 16)
 
+#define VIC_TFBIF_TRANSCFG     0x00002044
+#define  TRANSCFG_ATT(i, v)    (((v) & 0x3) << (i * 4))
+#define  TRANSCFG_SID_HW       0
+#define  TRANSCFG_SID_PHY      1
+#define  TRANSCFG_SID_FALCON   2
+
 /* Firmware offsets */
 
 #define VIC_UCODE_FCE_HEADER_OFFSET            (6*4)
index b4c385d..103fffc 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/debugfs.h>
 #include <linux/host1x.h>
 #include <linux/of.h>
+#include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <linux/of_device.h>
 
@@ -500,6 +502,36 @@ static void host1x_detach_driver(struct host1x *host1x,
        mutex_unlock(&host1x->devices_lock);
 }
 
+static int host1x_devices_show(struct seq_file *s, void *data)
+{
+       struct host1x *host1x = s->private;
+       struct host1x_device *device;
+
+       mutex_lock(&host1x->devices_lock);
+
+       list_for_each_entry(device, &host1x->devices, list) {
+               struct host1x_subdev *subdev;
+
+               seq_printf(s, "%s\n", dev_name(&device->dev));
+
+               mutex_lock(&device->subdevs_lock);
+
+               list_for_each_entry(subdev, &device->active, list)
+                       seq_printf(s, "  %pOFf: %s\n", subdev->np,
+                                  dev_name(subdev->client->dev));
+
+               list_for_each_entry(subdev, &device->subdevs, list)
+                       seq_printf(s, "  %pOFf:\n", subdev->np);
+
+               mutex_unlock(&device->subdevs_lock);
+       }
+
+       mutex_unlock(&host1x->devices_lock);
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(host1x_devices);
+
 /**
  * host1x_register() - register a host1x controller
  * @host1x: host1x controller
@@ -523,6 +555,9 @@ int host1x_register(struct host1x *host1x)
 
        mutex_unlock(&drivers_lock);
 
+       debugfs_create_file("devices", S_IRUGO, host1x->debugfs, host1x,
+                           &host1x_devices_fops);
+
        return 0;
 }
 
index 91df51e..f45b7c6 100644 (file)
  * means that the push buffer is full, not empty.
  */
 
-#define HOST1X_PUSHBUFFER_SLOTS        512
+/*
+ * Typically the commands written into the push buffer are a pair of words. We
+ * use slots to represent each of these pairs and to simplify things. Note the
+ * strange number of slots allocated here. 512 slots will fit exactly within a
+ * single memory page. We also need one additional word at the end of the push
+ * buffer for the RESTART opcode that will instruct the CDMA to jump back to
+ * the beginning of the push buffer. With 512 slots, this means that we'll use
+ * 2 memory pages and waste 4092 bytes of the second page that will never be
+ * used.
+ */
+#define HOST1X_PUSHBUFFER_SLOTS        511
 
 /*
  * Clean up push buffer resources
@@ -143,7 +153,10 @@ static void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2)
        WARN_ON(pb->pos == pb->fence);
        *(p++) = op1;
        *(p++) = op2;
-       pb->pos = (pb->pos + 8) & (pb->size - 1);
+       pb->pos += 8;
+
+       if (pb->pos >= pb->size)
+               pb->pos -= pb->size;
 }
 
 /*
@@ -153,7 +166,10 @@ static void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2)
 static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots)
 {
        /* Advance the next write position */
-       pb->fence = (pb->fence + slots * 8) & (pb->size - 1);
+       pb->fence += slots * 8;
+
+       if (pb->fence >= pb->size)
+               pb->fence -= pb->size;
 }
 
 /*
@@ -161,7 +177,12 @@ static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots)
  */
 static u32 host1x_pushbuffer_space(struct push_buffer *pb)
 {
-       return ((pb->fence - pb->pos) & (pb->size - 1)) / 8;
+       unsigned int fence = pb->fence;
+
+       if (pb->fence < pb->pos)
+               fence += pb->size;
+
+       return (fence - pb->pos) / 8;
 }
 
 /*
@@ -210,13 +231,52 @@ unsigned int host1x_cdma_wait_locked(struct host1x_cdma *cdma,
                cdma->event = event;
 
                mutex_unlock(&cdma->lock);
-               down(&cdma->sem);
+               wait_for_completion(&cdma->complete);
                mutex_lock(&cdma->lock);
        }
 
        return 0;
 }
 
+/*
+ * Sleep (if necessary) until the push buffer has enough free space.
+ *
+ * Must be called with the cdma lock held.
+ */
+int host1x_cdma_wait_pushbuffer_space(struct host1x *host1x,
+                                     struct host1x_cdma *cdma,
+                                     unsigned int needed)
+{
+       while (true) {
+               struct push_buffer *pb = &cdma->push_buffer;
+               unsigned int space;
+
+               space = host1x_pushbuffer_space(pb);
+               if (space >= needed)
+                       break;
+
+               trace_host1x_wait_cdma(dev_name(cdma_to_channel(cdma)->dev),
+                                      CDMA_EVENT_PUSH_BUFFER_SPACE);
+
+               host1x_hw_cdma_flush(host1x, cdma);
+
+               /* If somebody has managed to already start waiting, yield */
+               if (cdma->event != CDMA_EVENT_NONE) {
+                       mutex_unlock(&cdma->lock);
+                       schedule();
+                       mutex_lock(&cdma->lock);
+                       continue;
+               }
+
+               cdma->event = CDMA_EVENT_PUSH_BUFFER_SPACE;
+
+               mutex_unlock(&cdma->lock);
+               wait_for_completion(&cdma->complete);
+               mutex_lock(&cdma->lock);
+       }
+
+       return 0;
+}
 /*
  * Start timer that tracks the time spent by the job.
  * Must be called with the cdma lock held.
@@ -314,7 +374,7 @@ static void update_cdma_locked(struct host1x_cdma *cdma)
 
        if (signal) {
                cdma->event = CDMA_EVENT_NONE;
-               up(&cdma->sem);
+               complete(&cdma->complete);
        }
 }
 
@@ -323,7 +383,7 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
 {
        struct host1x *host1x = cdma_to_host1x(cdma);
        u32 restart_addr, syncpt_incrs, syncpt_val;
-       struct host1x_job *job = NULL;
+       struct host1x_job *job, *next_job = NULL;
 
        syncpt_val = host1x_syncpt_load(cdma->timeout.syncpt);
 
@@ -341,40 +401,37 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
                __func__);
 
        list_for_each_entry(job, &cdma->sync_queue, list) {
-               if (syncpt_val < job->syncpt_end)
-                       break;
+               if (syncpt_val < job->syncpt_end) {
+
+                       if (!list_is_last(&job->list, &cdma->sync_queue))
+                               next_job = list_next_entry(job, list);
+
+                       goto syncpt_incr;
+               }
 
                host1x_job_dump(dev, job);
        }
 
+       /* all jobs have been completed */
+       job = NULL;
+
+syncpt_incr:
+
        /*
-        * Walk the sync_queue, first incrementing with the CPU syncpts that
-        * are partially executed (the first buffer) or fully skipped while
-        * still in the current context (slots are also NOP-ed).
+        * Increment with CPU the remaining syncpts of a partially executed job.
         *
-        * At the point contexts are interleaved, syncpt increments must be
-        * done inline with the pushbuffer from a GATHER buffer to maintain
-        * the order (slots are modified to be a GATHER of syncpt incrs).
-        *
-        * Note: save in restart_addr the location where the timed out buffer
-        * started in the PB, so we can start the refetch from there (with the
-        * modified NOP-ed PB slots). This lets things appear to have completed
-        * properly for this buffer and resources are freed.
+        * CDMA will continue execution starting with the next job or will get
+        * into idle state.
         */
-
-       dev_dbg(dev, "%s: perform CPU incr on pending same ctx buffers\n",
-               __func__);
-
-       if (!list_empty(&cdma->sync_queue))
-               restart_addr = job->first_get;
+       if (next_job)
+               restart_addr = next_job->first_get;
        else
                restart_addr = cdma->last_pos;
 
-       /* do CPU increments as long as this context continues */
-       list_for_each_entry_from(job, &cdma->sync_queue, list) {
-               /* different context, gets us out of this loop */
-               if (job->client != cdma->timeout.client)
-                       break;
+       /* do CPU increments for the remaining syncpts */
+       if (job) {
+               dev_dbg(dev, "%s: perform CPU incr on pending buffers\n",
+                       __func__);
 
                /* won't need a timeout when replayed */
                job->timeout = 0;
@@ -389,21 +446,10 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
                                                syncpt_incrs, job->syncpt_end,
                                                job->num_slots);
 
-               syncpt_val += syncpt_incrs;
+               dev_dbg(dev, "%s: finished sync_queue modification\n",
+                       __func__);
        }
 
-       /*
-        * The following sumbits from the same client may be dependent on the
-        * failed submit and therefore they may fail. Force a small timeout
-        * to make the queue cleanup faster.
-        */
-
-       list_for_each_entry_from(job, &cdma->sync_queue, list)
-               if (job->client == cdma->timeout.client)
-                       job->timeout = min_t(unsigned int, job->timeout, 500);
-
-       dev_dbg(dev, "%s: finished sync_queue modification\n", __func__);
-
        /* roll back DMAGET and start up channel again */
        host1x_hw_cdma_resume(host1x, cdma, restart_addr);
 }
@@ -416,7 +462,7 @@ int host1x_cdma_init(struct host1x_cdma *cdma)
        int err;
 
        mutex_init(&cdma->lock);
-       sema_init(&cdma->sem, 0);
+       init_completion(&cdma->complete);
 
        INIT_LIST_HEAD(&cdma->sync_queue);
 
@@ -509,6 +555,59 @@ void host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2)
        host1x_pushbuffer_push(pb, op1, op2);
 }
 
+/*
+ * Push four words into two consecutive push buffer slots. Note that extra
+ * care needs to be taken not to split the two slots across the end of the
+ * push buffer. Otherwise the RESTART opcode at the end of the push buffer
+ * that ensures processing will restart at the beginning will break up the
+ * four words.
+ *
+ * Blocks as necessary if the push buffer is full.
+ */
+void host1x_cdma_push_wide(struct host1x_cdma *cdma, u32 op1, u32 op2,
+                          u32 op3, u32 op4)
+{
+       struct host1x_channel *channel = cdma_to_channel(cdma);
+       struct host1x *host1x = cdma_to_host1x(cdma);
+       struct push_buffer *pb = &cdma->push_buffer;
+       unsigned int needed = 2, extra = 0, i;
+       unsigned int space = cdma->slots_free;
+
+       if (host1x_debug_trace_cmdbuf)
+               trace_host1x_cdma_push_wide(dev_name(channel->dev), op1, op2,
+                                           op3, op4);
+
+       /* compute number of extra slots needed for padding */
+       if (pb->pos + 16 > pb->size) {
+               extra = (pb->size - pb->pos) / 8;
+               needed += extra;
+       }
+
+       host1x_cdma_wait_pushbuffer_space(host1x, cdma, needed);
+       space = host1x_pushbuffer_space(pb);
+
+       cdma->slots_free = space - needed;
+       cdma->slots_used += needed;
+
+       /*
+        * Note that we rely on the fact that this is only used to submit wide
+        * gather opcodes, which consist of 3 words, and they are padded with
+        * a NOP to avoid having to deal with fractional slots (a slot always
+        * represents 2 words). The fourth opcode passed to this function will
+        * therefore always be a NOP.
+        *
+        * This works around a slight ambiguity when it comes to opcodes. For
+        * all current host1x incarnations the NOP opcode uses the exact same
+        * encoding (0x20000000), so we could hard-code the value here, but a
+        * new incarnation may change it and break that assumption.
+        */
+       for (i = 0; i < extra; i++)
+               host1x_pushbuffer_push(pb, op4, op4);
+
+       host1x_pushbuffer_push(pb, op1, op2);
+       host1x_pushbuffer_push(pb, op3, op4);
+}
+
 /*
  * End a cdma submit
  * Kick off DMA, add job to the sync queue, and a number of slots to be freed
index e97e17b..3a5e040 100644 (file)
@@ -20,7 +20,7 @@
 #define __HOST1X_CDMA_H
 
 #include <linux/sched.h>
-#include <linux/semaphore.h>
+#include <linux/completion.h>
 #include <linux/list.h>
 
 struct host1x_syncpt;
@@ -69,8 +69,8 @@ enum cdma_event {
 
 struct host1x_cdma {
        struct mutex lock;              /* controls access to shared state */
-       struct semaphore sem;           /* signalled when event occurs */
-       enum cdma_event event;          /* event that sem is waiting for */
+       struct completion complete;     /* signalled when event occurs */
+       enum cdma_event event;          /* event that complete is waiting for */
        unsigned int slots_used;        /* pb slots used in current submit */
        unsigned int slots_free;        /* pb slots free in current submit */
        unsigned int first_get;         /* DMAGET value, where submit begins */
@@ -90,6 +90,8 @@ int host1x_cdma_init(struct host1x_cdma *cdma);
 int host1x_cdma_deinit(struct host1x_cdma *cdma);
 int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job);
 void host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2);
+void host1x_cdma_push_wide(struct host1x_cdma *cdma, u32 op1, u32 op2,
+                          u32 op3, u32 op4);
 void host1x_cdma_end(struct host1x_cdma *cdma, struct host1x_job *job);
 void host1x_cdma_update(struct host1x_cdma *cdma);
 void host1x_cdma_peek(struct host1x_cdma *cdma, u32 dmaget, int slot,
index 419d892..ee3c7b8 100644 (file)
@@ -120,6 +120,15 @@ static const struct host1x_info host1x05_info = {
        .dma_mask = DMA_BIT_MASK(34),
 };
 
+static const struct host1x_sid_entry tegra186_sid_table[] = {
+       {
+               /* VIC */
+               .base = 0x1af0,
+               .offset = 0x30,
+               .limit = 0x34
+       },
+};
+
 static const struct host1x_info host1x06_info = {
        .nb_channels = 63,
        .nb_pts = 576,
@@ -127,8 +136,19 @@ static const struct host1x_info host1x06_info = {
        .nb_bases = 16,
        .init = host1x06_init,
        .sync_offset = 0x0,
-       .dma_mask = DMA_BIT_MASK(34),
+       .dma_mask = DMA_BIT_MASK(40),
        .has_hypervisor = true,
+       .num_sid_entries = ARRAY_SIZE(tegra186_sid_table),
+       .sid_table = tegra186_sid_table,
+};
+
+static const struct host1x_sid_entry tegra194_sid_table[] = {
+       {
+               /* VIC */
+               .base = 0x1af0,
+               .offset = 0x30,
+               .limit = 0x34
+       },
 };
 
 static const struct host1x_info host1x07_info = {
@@ -140,6 +160,8 @@ static const struct host1x_info host1x07_info = {
        .sync_offset = 0x0,
        .dma_mask = DMA_BIT_MASK(40),
        .has_hypervisor = true,
+       .num_sid_entries = ARRAY_SIZE(tegra194_sid_table),
+       .sid_table = tegra194_sid_table,
 };
 
 static const struct of_device_id host1x_of_match[] = {
@@ -154,6 +176,19 @@ static const struct of_device_id host1x_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, host1x_of_match);
 
+static void host1x_setup_sid_table(struct host1x *host)
+{
+       const struct host1x_info *info = host->info;
+       unsigned int i;
+
+       for (i = 0; i < info->num_sid_entries; i++) {
+               const struct host1x_sid_entry *entry = &info->sid_table[i];
+
+               host1x_hypervisor_writel(host, entry->offset, entry->base);
+               host1x_hypervisor_writel(host, entry->limit, entry->base + 4);
+       }
+}
+
 static int host1x_probe(struct platform_device *pdev)
 {
        struct host1x *host;
@@ -248,6 +283,8 @@ static int host1x_probe(struct platform_device *pdev)
        host->group = iommu_group_get(&pdev->dev);
        if (host->group) {
                struct iommu_domain_geometry *geometry;
+               u64 mask = dma_get_mask(host->dev);
+               dma_addr_t start, end;
                unsigned long order;
 
                err = iova_cache_get();
@@ -275,11 +312,12 @@ static int host1x_probe(struct platform_device *pdev)
                }
 
                geometry = &host->domain->geometry;
+               start = geometry->aperture_start & mask;
+               end = geometry->aperture_end & mask;
 
                order = __ffs(host->domain->pgsize_bitmap);
-               init_iova_domain(&host->iova, 1UL << order,
-                                geometry->aperture_start >> order);
-               host->iova_end = geometry->aperture_end;
+               init_iova_domain(&host->iova, 1UL << order, start >> order);
+               host->iova_end = end;
        }
 
 skip_iommu:
@@ -316,6 +354,9 @@ skip_iommu:
 
        host1x_debug_init(host);
 
+       if (host->info->has_hypervisor)
+               host1x_setup_sid_table(host);
+
        err = host1x_register(host);
        if (err < 0)
                goto fail_deinit_intr;
index 36f44ff..05216a7 100644 (file)
@@ -94,6 +94,12 @@ struct host1x_intr_ops {
        int (*free_syncpt_irq)(struct host1x *host);
 };
 
+struct host1x_sid_entry {
+       unsigned int base;
+       unsigned int offset;
+       unsigned int limit;
+};
+
 struct host1x_info {
        unsigned int nb_channels; /* host1x: number of channels supported */
        unsigned int nb_pts; /* host1x: number of syncpoints supported */
@@ -103,6 +109,8 @@ struct host1x_info {
        unsigned int sync_offset; /* offset of syncpoint registers */
        u64 dma_mask; /* mask of addressable memory */
        bool has_hypervisor; /* has hypervisor registers */
+       unsigned int num_sid_entries;
+       const struct host1x_sid_entry *sid_table;
 };
 
 struct host1x {
index ce32053..5d61088 100644 (file)
@@ -39,8 +39,6 @@ static void push_buffer_init(struct push_buffer *pb)
 static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr,
                                u32 syncpt_incrs, u32 syncval, u32 nr_slots)
 {
-       struct host1x *host1x = cdma_to_host1x(cdma);
-       struct push_buffer *pb = &cdma->push_buffer;
        unsigned int i;
 
        for (i = 0; i < syncpt_incrs; i++)
@@ -48,18 +46,6 @@ static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr,
 
        /* after CPU incr, ensure shadow is up to date */
        host1x_syncpt_load(cdma->timeout.syncpt);
-
-       /* NOP all the PB slots */
-       while (nr_slots--) {
-               u32 *p = (u32 *)(pb->mapped + getptr);
-               *(p++) = HOST1X_OPCODE_NOP;
-               *(p++) = HOST1X_OPCODE_NOP;
-               dev_dbg(host1x->dev, "%s: NOP at %pad+%#x\n", __func__,
-                       &pb->dma, getptr);
-               getptr = (getptr + 8) & (pb->size - 1);
-       }
-
-       wmb();
 }
 
 /*
@@ -68,20 +54,31 @@ static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr,
 static void cdma_start(struct host1x_cdma *cdma)
 {
        struct host1x_channel *ch = cdma_to_channel(cdma);
+       u64 start, end;
 
        if (cdma->running)
                return;
 
        cdma->last_pos = cdma->push_buffer.pos;
+       start = cdma->push_buffer.dma;
+       end = cdma->push_buffer.size + 4;
 
        host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
                         HOST1X_CHANNEL_DMACTRL);
 
        /* set base, put and end pointer */
-       host1x_ch_writel(ch, cdma->push_buffer.dma, HOST1X_CHANNEL_DMASTART);
+       host1x_ch_writel(ch, lower_32_bits(start), HOST1X_CHANNEL_DMASTART);
+#if HOST1X_HW >= 6
+       host1x_ch_writel(ch, upper_32_bits(start), HOST1X_CHANNEL_DMASTART_HI);
+#endif
        host1x_ch_writel(ch, cdma->push_buffer.pos, HOST1X_CHANNEL_DMAPUT);
-       host1x_ch_writel(ch, cdma->push_buffer.dma + cdma->push_buffer.size + 4,
-                        HOST1X_CHANNEL_DMAEND);
+#if HOST1X_HW >= 6
+       host1x_ch_writel(ch, 0, HOST1X_CHANNEL_DMAPUT_HI);
+#endif
+       host1x_ch_writel(ch, lower_32_bits(end), HOST1X_CHANNEL_DMAEND);
+#if HOST1X_HW >= 6
+       host1x_ch_writel(ch, upper_32_bits(end), HOST1X_CHANNEL_DMAEND_HI);
+#endif
 
        /* reset GET */
        host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP |
@@ -104,6 +101,7 @@ static void cdma_timeout_restart(struct host1x_cdma *cdma, u32 getptr)
 {
        struct host1x *host1x = cdma_to_host1x(cdma);
        struct host1x_channel *ch = cdma_to_channel(cdma);
+       u64 start, end;
 
        if (cdma->running)
                return;
@@ -113,10 +111,18 @@ static void cdma_timeout_restart(struct host1x_cdma *cdma, u32 getptr)
        host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
                         HOST1X_CHANNEL_DMACTRL);
 
+       start = cdma->push_buffer.dma;
+       end = cdma->push_buffer.size + 4;
+
        /* set base, end pointer (all of memory) */
-       host1x_ch_writel(ch, cdma->push_buffer.dma, HOST1X_CHANNEL_DMASTART);
-       host1x_ch_writel(ch, cdma->push_buffer.dma + cdma->push_buffer.size,
-                        HOST1X_CHANNEL_DMAEND);
+       host1x_ch_writel(ch, lower_32_bits(start), HOST1X_CHANNEL_DMASTART);
+#if HOST1X_HW >= 6
+       host1x_ch_writel(ch, upper_32_bits(start), HOST1X_CHANNEL_DMASTART_HI);
+#endif
+       host1x_ch_writel(ch, lower_32_bits(end), HOST1X_CHANNEL_DMAEND);
+#if HOST1X_HW >= 6
+       host1x_ch_writel(ch, upper_32_bits(end), HOST1X_CHANNEL_DMAEND_HI);
+#endif
 
        /* set GET, by loading the value in PUT (then reset GET) */
        host1x_ch_writel(ch, getptr, HOST1X_CHANNEL_DMAPUT);
index 95ea811..27101c0 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <linux/host1x.h>
+#include <linux/iommu.h>
 #include <linux/slab.h>
 
 #include <trace/events/host1x.h>
@@ -60,15 +61,37 @@ static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo,
 static void submit_gathers(struct host1x_job *job)
 {
        struct host1x_cdma *cdma = &job->channel->cdma;
+#if HOST1X_HW < 6
+       struct device *dev = job->channel->dev;
+#endif
        unsigned int i;
 
        for (i = 0; i < job->num_gathers; i++) {
                struct host1x_job_gather *g = &job->gathers[i];
-               u32 op1 = host1x_opcode_gather(g->words);
-               u32 op2 = g->base + g->offset;
+               dma_addr_t addr = g->base + g->offset;
+               u32 op2, op3;
+
+               op2 = lower_32_bits(addr);
+               op3 = upper_32_bits(addr);
+
+               trace_write_gather(cdma, g->bo, g->offset, g->words);
+
+               if (op3 != 0) {
+#if HOST1X_HW >= 6
+                       u32 op1 = host1x_opcode_gather_wide(g->words);
+                       u32 op4 = HOST1X_OPCODE_NOP;
+
+                       host1x_cdma_push_wide(cdma, op1, op2, op3, op4);
+#else
+                       dev_err(dev, "invalid gather for push buffer %pad\n",
+                               &addr);
+                       continue;
+#endif
+               } else {
+                       u32 op1 = host1x_opcode_gather(g->words);
 
-               trace_write_gather(cdma, g->bo, g->offset, op1 & 0xffff);
-               host1x_cdma_push(cdma, op1, op2);
+                       host1x_cdma_push(cdma, op1, op2);
+               }
        }
 }
 
@@ -89,6 +112,16 @@ static inline void synchronize_syncpt_base(struct host1x_job *job)
                         HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(value));
 }
 
+static void host1x_channel_set_streamid(struct host1x_channel *channel)
+{
+#if HOST1X_HW >= 6
+       struct iommu_fwspec *spec = dev_iommu_fwspec_get(channel->dev->parent);
+       u32 sid = spec ? spec->ids[0] & 0xffff : 0x7f;
+
+       host1x_ch_writel(channel, sid, HOST1X_CHANNEL_SMMU_STREAMID);
+#endif
+}
+
 static int channel_submit(struct host1x_job *job)
 {
        struct host1x_channel *ch = job->channel;
@@ -120,6 +153,8 @@ static int channel_submit(struct host1x_job *job)
                goto error;
        }
 
+       host1x_channel_set_streamid(ch);
+
        /* begin a CDMA submit */
        err = host1x_cdma_begin(&ch->cdma, job);
        if (err) {
index 3039c92..dd37b10 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/types.h>
 #include <linux/bitops.h>
 
+#include "hw_host1x06_channel.h"
 #include "hw_host1x06_uclass.h"
 #include "hw_host1x06_vm.h"
 #include "hw_host1x06_hypervisor.h"
@@ -137,6 +138,11 @@ static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count)
        return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count;
 }
 
+static inline u32 host1x_opcode_gather_wide(unsigned count)
+{
+       return (12 << 28) | count;
+}
+
 #define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0)
 
 #endif
index 1353e7a..9f6da4e 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/types.h>
 #include <linux/bitops.h>
 
+#include "hw_host1x07_channel.h"
 #include "hw_host1x07_uclass.h"
 #include "hw_host1x07_vm.h"
 #include "hw_host1x07_hypervisor.h"
@@ -137,6 +138,11 @@ static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count)
        return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count;
 }
 
+static inline u32 host1x_opcode_gather_wide(unsigned count)
+{
+       return (12 << 28) | count;
+}
+
 #define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0)
 
 #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 (file)
index 0000000..18ae1c5
--- /dev/null
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 NVIDIA Corporation.
+ */
+
+#ifndef HOST1X_HW_HOST1X06_CHANNEL_H
+#define HOST1X_HW_HOST1X06_CHANNEL_H
+
+#define HOST1X_CHANNEL_SMMU_STREAMID 0x084
+
+#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 (file)
index 0000000..96fa72b
--- /dev/null
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 NVIDIA Corporation.
+ */
+
+#ifndef HOST1X_HW_HOST1X07_CHANNEL_H
+#define HOST1X_HW_HOST1X07_CHANNEL_H
+
+#define HOST1X_CHANNEL_SMMU_STREAMID 0x084
+
+#endif
index a37ef73..3d340b6 100644 (file)
@@ -80,6 +80,32 @@ TRACE_EVENT(host1x_cdma_push,
                __entry->name, __entry->op1, __entry->op2)
 );
 
+TRACE_EVENT(host1x_cdma_push_wide,
+       TP_PROTO(const char *name, u32 op1, u32 op2, u32 op3, u32 op4),
+
+       TP_ARGS(name, op1, op2, op3, op4),
+
+       TP_STRUCT__entry(
+               __field(const char *, name)
+               __field(u32, op1)
+               __field(u32, op2)
+               __field(u32, op3)
+               __field(u32, op4)
+       ),
+
+       TP_fast_assign(
+               __entry->name = name;
+               __entry->op1 = op1;
+               __entry->op2 = op2;
+               __entry->op3 = op3;
+               __entry->op4 = op4;
+       ),
+
+       TP_printk("name=%s, op1=%08x, op2=%08x, op3=%08x op4=%08x",
+               __entry->name, __entry->op1, __entry->op2, __entry->op3,
+               __entry->op4)
+);
+
 TRACE_EVENT(host1x_cdma_push_gather,
        TP_PROTO(const char *name, struct host1x_bo *bo,
                        u32 words, u32 offset, void *cmdbuf),