Commit 904ce198 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm/tegra/for-5.5-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next

drm/tegra: Changes for v5.5-rc1

The bulk of these changes is the addition of DisplayPort support for
Tegra210, Tegra186 and Tegra194. I've been running versions of this for
about three years now, so I'd consider these changes to be pretty
mature. These changes also unify the existing eDP support with the DP
support since the programming is very similar, except for a few steps
that can be easily parameterized.

The rest are a couple of fixes all over the place for minor issues, as
well as some work to support the IOMMU-backed DMA API, which in the end
turned out to also clean up a number of cases where the DMA API was not
being used correctly.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Thierry Reding <thierry.reding@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191102140116.3860545-1-thierry.reding@gmail.com
parents 633aa7e5 84db889e
......@@ -9,7 +9,7 @@ config DRM_TEGRA
select DRM_MIPI_DSI
select DRM_PANEL
select TEGRA_HOST1X
select IOMMU_IOVA if IOMMU_SUPPORT
select IOMMU_IOVA
select CEC_CORE if CEC_NOTIFIER
help
Choose this option if you have an NVIDIA Tegra SoC.
......
......@@ -715,9 +715,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane,
window.swap = state->swap;
for (i = 0; i < fb->format->num_planes; i++) {
struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
window.base[i] = bo->paddr + fb->offsets[i];
window.base[i] = state->iova[i] + fb->offsets[i];
/*
* Tegra uses a shared stride for UV planes. Framebuffers are
......@@ -732,6 +730,8 @@ static void tegra_plane_atomic_update(struct drm_plane *plane,
}
static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = {
.prepare_fb = tegra_plane_prepare_fb,
.cleanup_fb = tegra_plane_cleanup_fb,
.atomic_check = tegra_plane_atomic_check,
.atomic_disable = tegra_plane_atomic_disable,
.atomic_update = tegra_plane_atomic_update,
......@@ -869,11 +869,11 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
return;
}
value |= (bo->paddr >> 10) & 0x3fffff;
value |= (bo->iova >> 10) & 0x3fffff;
tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
value = (bo->paddr >> 32) & 0x3;
value = (bo->iova >> 32) & 0x3;
tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
#endif
......@@ -914,6 +914,8 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane,
}
static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
.prepare_fb = tegra_plane_prepare_fb,
.cleanup_fb = tegra_plane_cleanup_fb,
.atomic_check = tegra_cursor_atomic_check,
.atomic_update = tegra_cursor_atomic_update,
.atomic_disable = tegra_cursor_atomic_disable,
......@@ -2014,9 +2016,8 @@ static int tegra_dc_init(struct host1x_client *client)
if (!dc->syncpt)
dev_warn(dc->dev, "failed to allocate syncpoint\n");
dc->group = host1x_client_iommu_attach(client, true);
if (IS_ERR(dc->group)) {
err = PTR_ERR(dc->group);
err = host1x_client_iommu_attach(client);
if (err < 0) {
dev_err(client->dev, "failed to attach to domain: %d\n", err);
return err;
}
......@@ -2074,6 +2075,12 @@ static int tegra_dc_init(struct host1x_client *client)
goto cleanup;
}
/*
* Inherit the DMA parameters (such as maximum segment size) from the
* parent device.
*/
client->dev->dma_parms = client->parent->dma_parms;
return 0;
cleanup:
......@@ -2083,7 +2090,7 @@ static int tegra_dc_init(struct host1x_client *client)
if (!IS_ERR(primary))
drm_plane_cleanup(primary);
host1x_client_iommu_detach(client, dc->group);
host1x_client_iommu_detach(client);
host1x_syncpt_free(dc->syncpt);
return err;
......@@ -2097,6 +2104,9 @@ static int tegra_dc_exit(struct host1x_client *client)
if (!tegra_dc_has_window_groups(dc))
return 0;
/* avoid a dangling pointer just in case this disappears */
client->dev->dma_parms = NULL;
devm_free_irq(dc->dev, dc->irq, dc);
err = tegra_dc_rgb_exit(dc);
......@@ -2105,7 +2115,7 @@ static int tegra_dc_exit(struct host1x_client *client)
return err;
}
host1x_client_iommu_detach(client, dc->group);
host1x_client_iommu_detach(client);
host1x_syncpt_free(dc->syncpt);
return 0;
......
......@@ -90,8 +90,6 @@ struct tegra_dc {
struct drm_info_list *debugfs_files;
const struct tegra_dc_soc_info *soc;
struct iommu_group *group;
};
static inline struct tegra_dc *
......
This diff is collapsed.
......@@ -7,20 +7,171 @@
#ifndef DRM_TEGRA_DP_H
#define DRM_TEGRA_DP_H 1
#include <linux/types.h>
struct drm_display_info;
struct drm_display_mode;
struct drm_dp_aux;
struct drm_dp_link;
/**
* struct drm_dp_link_caps - DP link capabilities
*/
struct drm_dp_link_caps {
/**
* @enhanced_framing:
*
* enhanced framing capability (mandatory as of DP 1.2)
*/
bool enhanced_framing;
/**
* tps3_supported:
*
* training pattern sequence 3 supported for equalization
*/
bool tps3_supported;
/**
* @fast_training:
*
* AUX CH handshake not required for link training
*/
bool fast_training;
/**
* @channel_coding:
*
* ANSI 8B/10B channel coding capability
*/
bool channel_coding;
/**
* @alternate_scrambler_reset:
*
* eDP alternate scrambler reset capability
*/
bool alternate_scrambler_reset;
};
void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
const struct drm_dp_link_caps *src);
/**
* struct drm_dp_link_ops - DP link operations
*/
struct drm_dp_link_ops {
/**
* @apply_training:
*/
int (*apply_training)(struct drm_dp_link *link);
/**
* @configure:
*/
int (*configure)(struct drm_dp_link *link);
};
#define DP_TRAIN_VOLTAGE_SWING_LEVEL(x) ((x) << 0)
#define DP_TRAIN_PRE_EMPHASIS_LEVEL(x) ((x) << 3)
#define DP_LANE_POST_CURSOR(i, x) (((x) & 0x3) << (((i) & 1) << 2))
/**
* struct drm_dp_link_train_set - link training settings
* @voltage_swing: per-lane voltage swing
* @pre_emphasis: per-lane pre-emphasis
* @post_cursor: per-lane post-cursor
*/
struct drm_dp_link_train_set {
unsigned int voltage_swing[4];
unsigned int pre_emphasis[4];
unsigned int post_cursor[4];
};
/**
* struct drm_dp_link_train - link training state information
* @request: currently requested settings
* @adjust: adjustments requested by sink
* @pattern: currently requested training pattern
* @clock_recovered: flag to track if clock recovery has completed
* @channel_equalized: flag to track if channel equalization has completed
*/
struct drm_dp_link_train {
struct drm_dp_link_train_set request;
struct drm_dp_link_train_set adjust;
unsigned int pattern;
#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0)
bool clock_recovered;
bool channel_equalized;
};
/**
* struct drm_dp_link - DP link capabilities and configuration
* @revision: DP specification revision supported on the link
* @max_rate: maximum clock rate supported on the link
* @max_lanes: maximum number of lanes supported on the link
* @caps: capabilities supported on the link (see &drm_dp_link_caps)
* @aux_rd_interval: AUX read interval to use for training (in microseconds)
* @edp: eDP revision (0x11: eDP 1.1, 0x12: eDP 1.2, ...)
* @rate: currently configured link rate
* @lanes: currently configured number of lanes
* @rates: additional supported link rates in kHz (eDP 1.4)
* @num_rates: number of additional supported link rates (eDP 1.4)
*/
struct drm_dp_link {
unsigned char revision;
unsigned int max_rate;
unsigned int max_lanes;
struct drm_dp_link_caps caps;
/**
* @cr: clock recovery read interval
* @ce: channel equalization read interval
*/
struct {
unsigned int cr;
unsigned int ce;
} aux_rd_interval;
unsigned char edp;
unsigned int rate;
unsigned int num_lanes;
unsigned long capabilities;
unsigned int lanes;
unsigned long rates[DP_MAX_SUPPORTED_RATES];
unsigned int num_rates;
/**
* @ops: DP link operations
*/
const struct drm_dp_link_ops *ops;
/**
* @aux: DP AUX channel
*/
struct drm_dp_aux *aux;
/**
* @train: DP link training state
*/
struct drm_dp_link_train train;
};
int drm_dp_link_add_rate(struct drm_dp_link *link, unsigned long rate);
int drm_dp_link_remove_rate(struct drm_dp_link *link, unsigned long rate);
void drm_dp_link_update_rates(struct drm_dp_link *link);
int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link);
int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link);
int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link);
int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link);
int drm_dp_link_choose(struct drm_dp_link *link,
const struct drm_display_mode *mode,
const struct drm_display_info *info);
void drm_dp_link_train_init(struct drm_dp_link_train *train);
int drm_dp_link_train(struct drm_dp_link *link);
#endif
......@@ -9,6 +9,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
......@@ -30,10 +31,18 @@
static DEFINE_MUTEX(dpaux_lock);
static LIST_HEAD(dpaux_list);
struct tegra_dpaux_soc {
unsigned int cmh;
unsigned int drvz;
unsigned int drvi;
};
struct tegra_dpaux {
struct drm_dp_aux aux;
struct device *dev;
const struct tegra_dpaux_soc *soc;
void __iomem *regs;
int irq;
......@@ -121,6 +130,7 @@ static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
struct tegra_dpaux *dpaux = to_dpaux(aux);
unsigned long status;
ssize_t ret = 0;
u8 reply = 0;
u32 value;
/* Tegra has 4x4 byte DP AUX transmit and receive FIFOs. */
......@@ -215,23 +225,23 @@ static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
switch ((value & DPAUX_DP_AUXSTAT_REPLY_TYPE_MASK) >> 16) {
case 0x00:
msg->reply = DP_AUX_NATIVE_REPLY_ACK;
reply = DP_AUX_NATIVE_REPLY_ACK;
break;
case 0x01:
msg->reply = DP_AUX_NATIVE_REPLY_NACK;
reply = DP_AUX_NATIVE_REPLY_NACK;
break;
case 0x02:
msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
reply = DP_AUX_NATIVE_REPLY_DEFER;
break;
case 0x04:
msg->reply = DP_AUX_I2C_REPLY_NACK;
reply = DP_AUX_I2C_REPLY_NACK;
break;
case 0x08:
msg->reply = DP_AUX_I2C_REPLY_DEFER;
reply = DP_AUX_I2C_REPLY_DEFER;
break;
}
......@@ -239,14 +249,24 @@ static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
if (msg->request & DP_AUX_I2C_READ) {
size_t count = value & DPAUX_DP_AUXSTAT_REPLY_MASK;
if (WARN_ON(count != msg->size))
count = min_t(size_t, count, msg->size);
/*
* There might be a smarter way to do this, but since
* the DP helpers will already retry transactions for
* an -EBUSY return value, simply reuse that instead.
*/
if (count != msg->size) {
ret = -EBUSY;
goto out;
}
tegra_dpaux_read_fifo(dpaux, msg->buffer, count);
ret = count;
}
}
msg->reply = reply;
out:
return ret;
}
......@@ -311,9 +331,9 @@ static int tegra_dpaux_pad_config(struct tegra_dpaux *dpaux, unsigned function)
switch (function) {
case DPAUX_PADCTL_FUNC_AUX:
value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) |
DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) |
DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) |
value = DPAUX_HYBRID_PADCTL_AUX_CMH(dpaux->soc->cmh) |
DPAUX_HYBRID_PADCTL_AUX_DRVZ(dpaux->soc->drvz) |
DPAUX_HYBRID_PADCTL_AUX_DRVI(dpaux->soc->drvi) |
DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV |
DPAUX_HYBRID_PADCTL_MODE_AUX;
break;
......@@ -321,9 +341,9 @@ static int tegra_dpaux_pad_config(struct tegra_dpaux *dpaux, unsigned function)
case DPAUX_PADCTL_FUNC_I2C:
value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV |
DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV |
DPAUX_HYBRID_PADCTL_AUX_CMH(2) |
DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) |
DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) |
DPAUX_HYBRID_PADCTL_AUX_CMH(dpaux->soc->cmh) |
DPAUX_HYBRID_PADCTL_AUX_DRVZ(dpaux->soc->drvz) |
DPAUX_HYBRID_PADCTL_AUX_DRVI(dpaux->soc->drvi) |
DPAUX_HYBRID_PADCTL_MODE_I2C;
break;
......@@ -437,6 +457,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
if (!dpaux)
return -ENOMEM;
dpaux->soc = of_device_get_match_data(&pdev->dev);
INIT_WORK(&dpaux->work, tegra_dpaux_hotplug);
init_completion(&dpaux->complete);
INIT_LIST_HEAD(&dpaux->list);
......@@ -494,6 +515,8 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
return PTR_ERR(dpaux->vdd);
}
dpaux->vdd = NULL;
}
platform_set_drvdata(pdev, dpaux);
......@@ -642,11 +665,29 @@ static const struct dev_pm_ops tegra_dpaux_pm_ops = {
SET_RUNTIME_PM_OPS(tegra_dpaux_suspend, tegra_dpaux_resume, NULL)
};
static const struct tegra_dpaux_soc tegra124_dpaux_soc = {
.cmh = 0x02,
.drvz = 0x04,
.drvi = 0x18,
};
static const struct tegra_dpaux_soc tegra210_dpaux_soc = {
.cmh = 0x02,
.drvz = 0x04,
.drvi = 0x30,
};
static const struct tegra_dpaux_soc tegra194_dpaux_soc = {
.cmh = 0x02,
.drvz = 0x04,
.drvi = 0x2c,
};
static const struct of_device_id tegra_dpaux_of_match[] = {
{ .compatible = "nvidia,tegra194-dpaux", },
{ .compatible = "nvidia,tegra186-dpaux", },
{ .compatible = "nvidia,tegra210-dpaux", },
{ .compatible = "nvidia,tegra124-dpaux", },
{ .compatible = "nvidia,tegra194-dpaux", .data = &tegra194_dpaux_soc },
{ .compatible = "nvidia,tegra186-dpaux", .data = &tegra210_dpaux_soc },
{ .compatible = "nvidia,tegra210-dpaux", .data = &tegra210_dpaux_soc },
{ .compatible = "nvidia,tegra124-dpaux", .data = &tegra124_dpaux_soc },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_dpaux_of_match);
......@@ -687,25 +728,32 @@ int drm_dp_aux_attach(struct drm_dp_aux *aux, struct tegra_output *output)
output->connector.polled = DRM_CONNECTOR_POLL_HPD;
dpaux->output = output;
err = regulator_enable(dpaux->vdd);
if (err < 0)
return err;
if (output->panel) {
enum drm_connector_status status;
timeout = jiffies + msecs_to_jiffies(250);
if (dpaux->vdd) {
err = regulator_enable(dpaux->vdd);
if (err < 0)
return err;
}
while (time_before(jiffies, timeout)) {
enum drm_connector_status status;
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
status = drm_dp_aux_detect(aux);
if (status == connector_status_connected)
break;
status = drm_dp_aux_detect(aux);
if (status == connector_status_connected) {
enable_irq(dpaux->irq);
return 0;
usleep_range(1000, 2000);
}
usleep_range(1000, 2000);
if (status != connector_status_connected)
return -ETIMEDOUT;
}
return -ETIMEDOUT;
enable_irq(dpaux->irq);
return 0;
}
int drm_dp_aux_detach(struct drm_dp_aux *aux)
......@@ -716,25 +764,33 @@ int drm_dp_aux_detach(struct drm_dp_aux *aux)
disable_irq(dpaux->irq);
err = regulator_disable(dpaux->vdd);
if (err < 0)
return err;
if (dpaux->output->panel) {
enum drm_connector_status status;
timeout = jiffies + msecs_to_jiffies(250);
if (dpaux->vdd) {
err = regulator_disable(dpaux->vdd);
if (err < 0)
return err;
}
while (time_before(jiffies, timeout)) {
enum drm_connector_status status;
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
status = drm_dp_aux_detect(aux);
if (status == connector_status_disconnected)
break;
status = drm_dp_aux_detect(aux);
if (status == connector_status_disconnected) {
dpaux->output = NULL;
return 0;
usleep_range(1000, 2000);
}
usleep_range(1000, 2000);
if (status != connector_status_disconnected)
return -ETIMEDOUT;
dpaux->output = NULL;
}
return -ETIMEDOUT;
return 0;
}
enum drm_connector_status drm_dp_aux_detect(struct drm_dp_aux *aux)
......@@ -765,72 +821,3 @@ int drm_dp_aux_disable(struct drm_dp_aux *aux)
return 0;
}
int drm_dp_aux_prepare(struct drm_dp_aux *aux, u8 encoding)
{
int err;
err = drm_dp_dpcd_writeb(aux, DP_MAIN_LINK_CHANNEL_CODING_SET,
encoding);
if (err < 0)
return err;
return 0;
}
int drm_dp_aux_train(struct drm_dp_aux *aux, struct drm_dp_link *link,
u8 pattern)
{
u8 tp = pattern & DP_TRAINING_PATTERN_MASK;
u8 status[DP_LINK_STATUS_SIZE], values[4];
unsigned int i;
int err;
err = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, pattern);
if (err < 0)
return err;
if (tp == DP_TRAINING_PATTERN_DISABLE)
return 0;
for (i = 0; i < link->num_lanes; i++)
values[i] = DP_TRAIN_MAX_PRE_EMPHASIS_REACHED |
DP_TRAIN_PRE_EMPH_LEVEL_0 |
DP_TRAIN_MAX_SWING_REACHED |
DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
err = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_SET, values,
link->num_lanes);
if (err < 0)
return err;
usleep_range(500, 1000);
err = drm_dp_dpcd_read_link_status(aux, status);
if (err < 0)
return err;
switch (tp) {
case DP_TRAINING_PATTERN_1:
if (!drm_dp_clock_recovery_ok(status, link->num_lanes))
return -EAGAIN;
break;
case DP_TRAINING_PATTERN_2:
if (!drm_dp_channel_eq_ok(status, link->num_lanes))
return -EAGAIN;
break;
default:
dev_err(aux->dev, "unsupported training pattern %u\n", tp);
return -EINVAL;
}
err = drm_dp_dpcd_writeb(aux, DP_EDP_CONFIGURATION_SET, 0);
if (err < 0)
return err;
return 0;
}
This diff is collapsed.
......@@ -36,7 +36,7 @@ struct tegra_drm {
struct drm_device *drm;
struct iommu_domain *domain;
struct iommu_group *group;
bool use_explicit_iommu;
struct mutex mm_lock;
struct drm_mm mm;
......@@ -100,10 +100,8 @@ int tegra_drm_register_client(struct tegra_drm *tegra,
struct tegra_drm_client *client);
int tegra_drm_unregister_client(struct tegra_drm *tegra,
struct tegra_drm_client *client);
struct iommu_group *host1x_client_iommu_attach(struct host1x_client *client,
bool shared);
void host1x_client_iommu_detach(struct host1x_client *client,
struct iommu_group *group);
int host1x_client_iommu_attach(struct host1x_client *client);
void host1x_client_iommu_detach(struct host1x_client *client);
int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm);
int tegra_drm_exit(struct tegra_drm *tegra);
......@@ -155,17 +153,12 @@ void tegra_output_connector_destroy(struct drm_connector *connector);
void tegra_output_encoder_destroy(struct drm_encoder *encoder);
/* from dpaux.c */
struct drm_dp_link;
struct drm_dp_aux *drm_dp_aux_find_by_of_node(struct device_node *np);
enum drm_connector_status drm_dp_aux_detect(struct drm_dp_aux *aux);
int drm_dp_aux_attach(struct drm_dp_aux *aux, struct tegra_output *output);
int drm_dp_aux_detach(struct drm_dp_aux *aux);
int drm_dp_aux_enable(struct drm_dp_aux *aux);
int drm_dp_aux_disable(struct drm_dp_aux *aux);
int drm_dp_aux_prepare(struct drm_dp_aux *aux, u8 encoding);
int drm_dp_aux_train(struct drm_dp_aux *aux, struct drm_dp_link *link,
u8 pattern);
/* from fb.c */
struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
......
......@@ -58,32 +58,17 @@ static int falcon_copy_chunk(struct falcon *falcon,
static void falcon_copy_firmware_image(struct falcon *falcon,
const struct firmware *firmware)
{
u32 *firmware_vaddr = falcon->firmware.vaddr;
dma_addr_t daddr;
u32 *virt = falcon->firmware.virt;
size_t i;
int err;
/* copy the whole thing taking into account endianness */
for (i = 0; i < firmware->size / sizeof(u32); i++)
firmware_vaddr[i] = le32_to_cpu(((u32 *)firmware->data)[i]);
/* ensure that caches are flushed and falcon can see the firmware */
daddr = dma_map_single(falcon->dev, firmware_vaddr,
falcon->firmware.size, DMA_TO_DEVICE);
err = dma_mapping_error(falcon->dev, daddr);
if (err) {
dev_err(falcon->dev, "failed to map firmware: %d\n", err);
return;
}
dma_sync_single_for_device(falcon->dev, daddr,
falcon->firmware.size, DMA_TO_DEVICE);
dma_unmap_single(falcon->dev, daddr, falcon->firmware.size,
DMA_TO_DEVICE);
virt[i] = le32_to_cpu(((u32 *)firmware->data)[i]);
}
static int falcon_parse_firmware_image(struct falcon *falcon)
{
struct falcon_fw_bin_header_v1 *bin = (void *)falcon->firmware.vaddr;
struct falcon_fw_bin_header_v1 *bin = (void *)falcon->firmware.virt;
struct falcon_fw_os_header_v1 *os;
/* endian problems would show up right here */
......@@ -104,7 +89,7 @@ static int falcon_parse_firmware_image(struct falcon *falcon)
return -EINVAL;
}
os = falcon->firmware.vaddr + bin->os_header_offset;
os = falcon->firmware.virt + bin->os_header_offset;
falcon->firmware.bin_data.size = bin->os_size;
falcon->firmware.bin_data.offset = bin->os_data_offset;
......@@ -125,6 +110,8 @@ int falcon_read_firmware(struct falcon *falcon, const char *name)
if (err < 0)
return err;
falcon->firmware.size = falcon->firmware.firmware->size;
return 0;
}
......@@ -133,16 +120,6 @@ int falcon_load_firmware(struct falcon *falcon)
const struct firmware *firmware = falcon->firmware.firmware;
int err;
falcon->firmware.size = firmware->size;
/* allocate iova space for the firmware */
falcon->firmware.vaddr = falcon->ops->alloc(falcon, firmware->size,
&falcon->firmware.paddr);
if (IS_ERR(falcon->firmware.vaddr)) {
dev_err(falcon->dev, "DMA memory mapping failed\n");
return PTR_ERR(falcon->firmware.vaddr);
}
/* copy firmware image into local area. this also ensures endianness */
falcon_copy_firmware_image(falcon, firmware);
......@@ -150,45 +127,26 @@ int falcon_load_firmware(struct falcon *falcon)
err = falcon_parse_firmware_image(falcon);
if (err < 0) {
dev_err(falcon->dev, "failed to parse firmware image\n");
goto err_setup_firmware_image;
return err;
}
release_firmware(firmware);
falcon->firmware.firmware = NULL;
return 0;
err_setup_firmware_image:
falcon->ops->free(falcon, falcon->firmware.size,
falcon->firmware.paddr, falcon->firmware.vaddr);
return err;
}
int falcon_init(struct falcon *falcon)
{
/* check mandatory ops */
if (!falcon->ops || !falcon->ops->alloc || !falcon->ops->free)
return -EINVAL;
falcon->firmware.vaddr = NULL;
falcon->firmware.virt = NULL;
return 0;
}
void falcon_exit(struct falcon *falcon)
{
if (falcon->firmware.firmware) {
if (falcon->firmware.firmware)
release_firmware(falcon->firmware.firmware);
falcon->firmware.firmware = NULL;
}
if (falcon->firmware.vaddr) {
falcon->ops->free(falcon, falcon->firmware.size,
falcon->firmware.paddr,
falcon->firmware.vaddr);
falcon->firmware.vaddr = NULL;
}
}
int falcon_boot(struct falcon *falcon)
......@@ -197,7 +155,7 @@ int falcon_boot(struct falcon *falcon)
u32 value;
int err;
if (!falcon->firmware.vaddr)
if (!falcon->firmware.virt)
return -EINVAL;
err = readl_poll_timeout(falcon->regs + FALCON_DMACTL, value,
......@@ -210,7 +168,7 @@ int falcon_boot(struct falcon *falcon)
falcon_writel(falcon, 0, FALCON_DMACTL);
/* setup the address of the binary data so Falcon can access it later */
falcon_writel(falcon, (falcon->firmware.paddr +
falcon_writel(falcon, (falcon->firmware.iova +
falcon->firmware.bin_data.offset) >> 8,
FALCON_DMATRFBASE);
......
......@@ -74,15 +74,6 @@ struct falcon_fw_os_header_v1 {
u32 data_size;
};
struct falcon;
struct falcon_ops {
void *(*alloc)(struct falcon *falcon, size_t size,
dma_addr_t *paddr);
void (*free)(struct falcon *falcon, size_t size,
dma_addr_t paddr, void *vaddr);
};
struct falcon_firmware_section {
unsigned long offset;
size_t size;
......@@ -93,8 +84,9 @@ struct falcon_firmware {
const struct firmware *firmware;
/* Raw firmware data */
dma_addr_t paddr;
void *vaddr;
dma_addr_t iova;
dma_addr_t phys;
void *virt;
size_t size;
/* Parsed firmware information */
......@@ -107,8 +99,6 @@ struct falcon {
/* Set by falcon client */
struct device *dev;
void __iomem *regs;
const struct falcon_ops *ops;
void *data;
struct falcon_firmware firmware;
};
......
......@@ -269,10 +269,10 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper,
}
}
drm->mode_config.fb_base = (resource_size_t)bo->paddr;
drm->mode_config.fb_base = (resource_size_t)bo->iova;
info->screen_base = (void __iomem *)bo->vaddr + offset;
info->screen_size = size;
info->fix.smem_start = (unsigned long)(bo->paddr + offset);
info->fix.smem_start = (unsigned long)(bo->iova + offset);
info->fix.smem_len = size;
return 0;
......
......@@ -27,17 +27,55 @@ static void tegra_bo_put(struct host1x_bo *bo)
drm_gem_object_put_unlocked(&obj->gem);
}
static dma_addr_t tegra_bo_pin(struct host1x_bo *bo, struct sg_table **sgt)
static struct sg_table *tegra_bo_pin(struct device *dev, struct host1x_bo *bo,
dma_addr_t *phys)
{
struct tegra_bo *obj = host1x_to_tegra_bo(bo);
struct sg_table *sgt;
int err;
/*
* If we've manually mapped the buffer object through the IOMMU, make
* sure to return the IOVA address of our mapping.
*/
if (phys && obj->mm) {
*phys = obj->iova;
return NULL;
}
/*
* If we don't have a mapping for this buffer yet, return an SG table
* so that host1x can do the mapping for us via the DMA API.
*/
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt)
return ERR_PTR(-ENOMEM);
*sgt = obj->sgt;
if (obj->pages) {
err = sg_alloc_table_from_pages(sgt, obj->pages, obj->num_pages,
0, obj->gem.size, GFP_KERNEL);
if (err < 0)
goto free;
} else {
err = dma_get_sgtable(dev, sgt, obj->vaddr, obj->iova,
obj->gem.size);
if (err < 0)
goto free;
}
return obj->paddr;
return sgt;
free:
kfree(sgt);
return ERR_PTR(err);
}
static void tegra_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
static void tegra_bo_unpin(struct device *dev, struct sg_table *sgt)
{
if (sgt) {
sg_free_table(sgt);
kfree(sgt);
}
}
static void *tegra_bo_mmap(struct host1x_bo *bo)
......@@ -133,9 +171,9 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo)
goto unlock;
}
bo->paddr = bo->mm->start;
bo->iova = bo->mm->start;
bo->size = iommu_map_sg(tegra->domain, bo->paddr, bo->sgt->sgl,
bo->size = iommu_map_sg(tegra->domain, bo->iova, bo->sgt->sgl,
bo->sgt->nents, prot);
if (!bo->size) {
dev_err(tegra->drm->dev, "failed to map buffer\n");
......@@ -161,7 +199,7 @@ static int tegra_bo_iommu_unmap(struct tegra_drm *tegra, struct tegra_bo *bo)
return 0;
mutex_lock(&tegra->mm_lock);
iommu_unmap(tegra->domain, bo->paddr, bo->size);
iommu_unmap(tegra->domain, bo->iova, bo->size);
drm_mm_remove_node(bo->mm);
mutex_unlock(&tegra->mm_lock);
......@@ -209,7 +247,7 @@ static void tegra_bo_free(struct drm_device *drm, struct tegra_bo *bo)
sg_free_table(bo->sgt);
kfree(bo->sgt);
} else if (bo->vaddr) {
dma_free_wc(drm->dev, bo->gem.size, bo->vaddr, bo->paddr);
dma_free_wc(drm->dev, bo->gem.size, bo->vaddr, bo->iova);
}
}
......@@ -264,7 +302,7 @@ static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo)
} else {
size_t size = bo->gem.size;
bo->vaddr = dma_alloc_wc(drm->dev, size, &bo->paddr,
bo->vaddr = dma_alloc_wc(drm->dev, size, &bo->iova,
GFP_KERNEL | __GFP_NOWARN);
if (!bo->vaddr) {
dev_err(drm->dev,
......@@ -365,7 +403,7 @@ static struct tegra_bo *tegra_bo_import(struct drm_device *drm,
goto detach;
}
bo->paddr = sg_dma_address(bo->sgt->sgl);
bo->iova = sg_dma_address(bo->sgt->sgl);
}
bo->gem.import_attach = attach;
......@@ -461,7 +499,7 @@ int __tegra_gem_mmap(struct drm_gem_object *gem, struct vm_area_struct *vma)
vma->vm_flags &= ~VM_PFNMAP;
vma->vm_pgoff = 0;
err = dma_mmap_wc(gem->dev->dev, vma, bo->vaddr, bo->paddr,
err = dma_mmap_wc(gem->dev->dev, vma, bo->vaddr, bo->iova,
gem->size);
if (err < 0) {
drm_gem_vm_close(vma);
......@@ -508,25 +546,18 @@ tegra_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
return NULL;
if (bo->pages) {
struct scatterlist *sg;
unsigned int i;
if (sg_alloc_table(sgt, bo->num_pages, GFP_KERNEL))
goto free;
for_each_sg(sgt->sgl, sg, bo->num_pages, i)
sg_set_page(sg, bo->pages[i], PAGE_SIZE, 0);
if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0)
if (sg_alloc_table_from_pages(sgt, bo->pages, bo->num_pages,
0, gem->size, GFP_KERNEL) < 0)
goto free;
} else {
if (sg_alloc_table(sgt, 1, GFP_KERNEL))
if (dma_get_sgtable(attach->dev, sgt, bo->vaddr, bo->iova,
gem->size) < 0)
goto free;
sg_dma_address(sgt->sgl) = bo->paddr;
sg_dma_len(sgt->sgl) = gem->size;
}
if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0)
goto free;
return sgt;
free:
......
......@@ -31,7 +31,7 @@ struct tegra_bo {
struct host1x_bo base;
unsigned long flags;
struct sg_table *sgt;
dma_addr_t paddr;
dma_addr_t iova;
void *vaddr;
struct drm_mm_node *mm;
......
......@@ -17,7 +17,6 @@ struct gr2d_soc {
};
struct gr2d {
struct iommu_group *group;
struct tegra_drm_client client;
struct host1x_channel *channel;
struct clk *clk;
......@@ -40,7 +39,7 @@ static int gr2d_init(struct host1x_client *client)
struct gr2d *gr2d = to_gr2d(drm);
int err;
gr2d->channel = host1x_channel_request(client->dev);
gr2d->channel = host1x_channel_request(client);
if (!gr2d->channel)
return -ENOMEM;
......@@ -51,9 +50,8 @@ static int gr2d_init(struct host1x_client *client)
goto put;
}
gr2d->group = host1x_client_iommu_attach(client, false);
if (IS_ERR(gr2d->group)) {
err = PTR_ERR(gr2d->group);
err = host1x_client_iommu_attach(client);
if (err < 0) {
dev_err(client->dev, "failed to attach to domain: %d\n", err);
goto free;
}
......@@ -67,7 +65,7 @@ static int gr2d_init(struct host1x_client *client)
return 0;
detach:
host1x_client_iommu_detach(client, gr2d->group);
host1x_client_iommu_detach(client);
free:
host1x_syncpt_free(client->syncpts[0]);
put:
......@@ -87,7 +85,7 @@ static int gr2d_exit(struct host1x_client *client)
if (err < 0)
return err;
host1x_client_iommu_detach(client, gr2d->group);
host1x_client_iommu_detach(client);
host1x_syncpt_free(client->syncpts[0]);
host1x_channel_put(gr2d->channel);
......
......@@ -23,7 +23,6 @@ struct gr3d_soc {
};
struct gr3d {
struct iommu_group *group;
struct tegra_drm_client client;
struct host1x_channel *channel;
struct clk *clk_secondary;
......@@ -49,7 +48,7 @@ static int gr3d_init(struct host1x_client *client)
struct gr3d *gr3d = to_gr3d(drm);
int err;
gr3d->channel = host1x_channel_request(client->dev);
gr3d->channel = host1x_channel_request(client);
if (!gr3d->channel)
return -ENOMEM;
......@@ -60,9 +59,8 @@ static int gr3d_init(struct host1x_client *client)
goto put;
}
gr3d->group = host1x_client_iommu_attach(client, false);
if (IS_ERR(gr3d->group)) {
err = PTR_ERR(gr3d->group);
err = host1x_client_iommu_attach(client);
if (err < 0) {
dev_err(client->dev, "failed to attach to domain: %d\n", err);
goto free;
}
......@@ -76,7 +74,7 @@ static int gr3d_init(struct host1x_client *client)
return 0;
detach:
host1x_client_iommu_detach(client, gr3d->group);
host1x_client_iommu_detach(client);
free:
host1x_syncpt_free(client->syncpts[0]);
put:
......@@ -95,7 +93,7 @@ static int gr3d_exit(struct host1x_client *client)
if (err < 0)
return err;
host1x_client_iommu_detach(client, gr3d->group);
host1x_client_iommu_detach(client);
host1x_syncpt_free(client->syncpts[0]);
host1x_channel_put(gr3d->channel);
......
......@@ -413,7 +413,6 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
unsigned int zpos = plane->state->normalized_zpos;
struct drm_framebuffer *fb = plane->state->fb;
struct tegra_plane *p = to_tegra_plane(plane);
struct tegra_bo *bo;
dma_addr_t base;
u32 value;
......@@ -456,8 +455,7 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
/* disable compression */
tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL);
bo = tegra_fb_get_plane(fb, 0);
base = bo->paddr;
base = state->iova[0] + fb->offsets[0];
tegra_plane_writel(p, state->format, DC_WIN_COLOR_DEPTH);
tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS);
......@@ -521,6 +519,8 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
}
static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = {
.prepare_fb = tegra_plane_prepare_fb,
.cleanup_fb = tegra_plane_cleanup_fb,
.atomic_check = tegra_shared_plane_atomic_check,
.atomic_update = tegra_shared_plane_atomic_update,
.atomic_disable = tegra_shared_plane_atomic_disable,
......
......@@ -70,6 +70,11 @@ tegra_output_connector_detect(struct drm_connector *connector, bool force)
void tegra_output_connector_destroy(struct drm_connector *connector)
{
struct tegra_output *output = connector_to_output(connector);
if (output->cec)
cec_notifier_conn_unregister(output->cec);
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
......@@ -163,18 +168,11 @@ 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 (output->hpd_gpio)
free_irq(output->hpd_irq, output);
......@@ -184,6 +182,7 @@ void tegra_output_remove(struct tegra_output *output)
int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
{
int connector_type;
int err;
if (output->panel) {
......@@ -199,6 +198,21 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
if (output->hpd_gpio)
enable_irq(output->hpd_irq);
connector_type = output->connector.connector_type;
/*
* Create a CEC notifier for HDMI connector.
*/
if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
connector_type == DRM_MODE_CONNECTOR_HDMIB) {
struct cec_connector_info conn_info;
cec_fill_conn_info_from_drm(&conn_info, &output->connector);
output->cec = cec_notifier_conn_register(output->dev, NULL,
&conn_info);
if (!output->cec)
return -ENOMEM;
}
return 0;
}
......
......@@ -6,6 +6,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_plane_helper.h>
#include "dc.h"
......@@ -23,6 +24,7 @@ static void tegra_plane_reset(struct drm_plane *plane)
{
struct tegra_plane *p = to_tegra_plane(plane);
struct tegra_plane_state *state;
unsigned int i;
if (plane->state)
__drm_atomic_helper_plane_destroy_state(plane->state);
......@@ -36,6 +38,9 @@ static void tegra_plane_reset(struct drm_plane *plane)
plane->state->plane = plane;
plane->state->zpos = p->index;
plane->state->normalized_zpos = p->index;
for (i = 0; i < 3; i++)
state->iova[i] = DMA_MAPPING_ERROR;
}
}
......@@ -60,6 +65,11 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
for (i = 0; i < 2; i++)
copy->blending[i] = state->blending[i];
for (i = 0; i < 3; i++) {
copy->iova[i] = DMA_MAPPING_ERROR;
copy->sgt[i] = NULL;
}
return &copy->base;
}
......@@ -95,6 +105,100 @@ const struct drm_plane_funcs tegra_plane_funcs = {
.format_mod_supported = tegra_plane_format_mod_supported,
};
static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state)
{
unsigned int i;
int err;
for (i = 0; i < state->base.fb->format->num_planes; i++) {
struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
if (!dc->client.group) {
struct sg_table *sgt;
sgt = host1x_bo_pin(dc->dev, &bo->base, NULL);
if (IS_ERR(sgt)) {
err = PTR_ERR(sgt);
goto unpin;
}
err = dma_map_sg(dc->dev, sgt->sgl, sgt->nents,
DMA_TO_DEVICE);
if (err == 0) {
err = -ENOMEM;
goto unpin;
}
state->iova[i] = sg_dma_address(sgt->sgl);
state->sgt[i] = sgt;
} else {
state->iova[i] = bo->iova;
}
}
return 0;
unpin:
dev_err(dc->dev, "failed to map plane %u: %d\n", i, err);
while (i--) {
struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
struct sg_table *sgt = state->sgt[i];
dma_unmap_sg(dc->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE);
host1x_bo_unpin(dc->dev, &bo->base, sgt);
state->iova[i] = DMA_MAPPING_ERROR;
state->sgt[i] = NULL;
}
return err;
}
static void tegra_dc_unpin(struct tegra_dc *dc, struct tegra_plane_state *state)
{
unsigned int i;
for (i = 0; i < state->base.fb->format->num_planes; i++) {
struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
if (!dc->client.group) {
struct sg_table *sgt = state->sgt[i];
if (sgt) {
dma_unmap_sg(dc->dev, sgt->sgl, sgt->nents,
DMA_TO_DEVICE);
host1x_bo_unpin(dc->dev, &bo->base, sgt);
}
}
state->iova[i] = DMA_MAPPING_ERROR;
state->sgt[i] = NULL;
}
}
int tegra_plane_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct tegra_dc *dc = to_tegra_dc(state->crtc);
if (!state->fb)
return 0;
drm_gem_fb_prepare_fb(plane, state);
return tegra_dc_pin(dc, to_tegra_plane_state(state));
}
void tegra_plane_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct tegra_dc *dc = to_tegra_dc(state->crtc);
if (dc)
tegra_dc_unpin(dc, to_tegra_plane_state(state));
}
int tegra_plane_state_add(struct tegra_plane *plane,
struct drm_plane_state *state)
{
......
......@@ -39,6 +39,9 @@ struct tegra_plane_legacy_blending_state {
struct tegra_plane_state {
struct drm_plane_state base;
struct sg_table *sgt[3];
dma_addr_t iova[3];
struct tegra_bo_tiling tiling;
u32 format;
u32 swap;
......@@ -61,6 +64,11 @@ to_tegra_plane_state(struct drm_plane_state *state)
extern const struct drm_plane_funcs tegra_plane_funcs;
int tegra_plane_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *state);
void tegra_plane_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *state);
int tegra_plane_state_add(struct tegra_plane *plane,
struct drm_plane_state *state);
......
This diff is collapsed.
......@@ -39,6 +39,7 @@
#define SOR_STATE_ASY_CRC_MODE_NON_ACTIVE (0x2 << 6)
#define SOR_STATE_ASY_CRC_MODE_COMPLETE (0x1 << 6)
#define SOR_STATE_ASY_CRC_MODE_ACTIVE (0x0 << 6)
#define SOR_STATE_ASY_SUBOWNER_MASK (0x3 << 4)
#define SOR_STATE_ASY_OWNER_MASK 0xf
#define SOR_STATE_ASY_OWNER(x) (((x) & 0xf) << 0)
......@@ -283,10 +284,12 @@
#define SOR_DP_PADCTL_CM_TXD_2 (1 << 6)
#define SOR_DP_PADCTL_CM_TXD_1 (1 << 5)
#define SOR_DP_PADCTL_CM_TXD_0 (1 << 4)
#define SOR_DP_PADCTL_CM_TXD(x) (1 << (4 + (x)))
#define SOR_DP_PADCTL_PD_TXD_3 (1 << 3)
#define SOR_DP_PADCTL_PD_TXD_0 (1 << 2)
#define SOR_DP_PADCTL_PD_TXD_1 (1 << 1)
#define SOR_DP_PADCTL_PD_TXD_2 (1 << 0)
#define SOR_DP_PADCTL_PD_TXD(x) (1 << (0 + (x)))
#define SOR_DP_PADCTL1 0x5d
......
......@@ -34,7 +34,6 @@ struct vic {
void __iomem *regs;
struct tegra_drm_client client;
struct host1x_channel *channel;
struct iommu_domain *domain;
struct device *dev;
struct clk *clk;
struct reset_control *rst;
......@@ -97,6 +96,9 @@ static int vic_runtime_suspend(struct device *dev)
static int vic_boot(struct vic *vic)
{
#ifdef CONFIG_IOMMU_API
struct iommu_fwspec *spec = dev_iommu_fwspec_get(vic->dev);
#endif
u32 fce_ucode_size, fce_bin_data_offset;
void *hdr;
int err = 0;
......@@ -105,15 +107,14 @@ static int vic_boot(struct vic *vic)
return 0;
#ifdef CONFIG_IOMMU_API
if (vic->config->supports_sid) {
struct iommu_fwspec *spec = dev_iommu_fwspec_get(vic->dev);
if (vic->config->supports_sid && spec) {
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) {
if (spec->num_ids > 0) {
value = spec->ids[0] & 0xffff;
vic_writel(vic, value, VIC_THI_STREAMID0);
......@@ -132,9 +133,9 @@ static int vic_boot(struct vic *vic)
if (err < 0)
return err;
hdr = vic->falcon.firmware.vaddr;
hdr = vic->falcon.firmware.virt;
fce_bin_data_offset = *(u32 *)(hdr + VIC_UCODE_FCE_DATA_OFFSET);
hdr = vic->falcon.firmware.vaddr +
hdr = vic->falcon.firmware.virt +
*(u32 *)(hdr + VIC_UCODE_FCE_HEADER_OFFSET);
fce_ucode_size = *(u32 *)(hdr + FCE_UCODE_SIZE_OFFSET);
......@@ -142,7 +143,7 @@ static int vic_boot(struct vic *vic)
falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_SIZE,
fce_ucode_size);
falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_OFFSET,
(vic->falcon.firmware.paddr + fce_bin_data_offset)
(vic->falcon.firmware.iova + fce_bin_data_offset)
>> 8);
err = falcon_wait_idle(&vic->falcon);
......@@ -157,48 +158,21 @@ static int vic_boot(struct vic *vic)
return 0;
}
static void *vic_falcon_alloc(struct falcon *falcon, size_t size,
dma_addr_t *iova)
{
struct tegra_drm *tegra = falcon->data;
return tegra_drm_alloc(tegra, size, iova);
}
static void vic_falcon_free(struct falcon *falcon, size_t size,
dma_addr_t iova, void *va)
{
struct tegra_drm *tegra = falcon->data;
return tegra_drm_free(tegra, size, va, iova);
}
static const struct falcon_ops vic_falcon_ops = {
.alloc = vic_falcon_alloc,
.free = vic_falcon_free
};
static int vic_init(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct iommu_group *group = iommu_group_get(client->dev);
struct drm_device *dev = dev_get_drvdata(client->parent);
struct tegra_drm *tegra = dev->dev_private;
struct vic *vic = to_vic(drm);
int err;
if (group && tegra->domain) {
err = iommu_attach_group(tegra->domain, group);
if (err < 0) {
dev_err(vic->dev, "failed to attach to domain: %d\n",
err);
return err;
}
vic->domain = tegra->domain;
err = host1x_client_iommu_attach(client);
if (err < 0) {
dev_err(vic->dev, "failed to attach to domain: %d\n", err);
return err;
}
vic->channel = host1x_channel_request(client->dev);
vic->channel = host1x_channel_request(client);
if (!vic->channel) {
err = -ENOMEM;
goto detach;
......@@ -214,6 +188,12 @@ static int vic_init(struct host1x_client *client)
if (err < 0)
goto free_syncpt;
/*
* Inherit the DMA parameters (such as maximum segment size) from the
* parent device.
*/
client->dev->dma_parms = client->parent->dma_parms;
return 0;
free_syncpt:
......@@ -221,8 +201,7 @@ static int vic_init(struct host1x_client *client)
free_channel:
host1x_channel_put(vic->channel);
detach:
if (group && tegra->domain)
iommu_detach_group(tegra->domain, group);
host1x_client_iommu_detach(client);
return err;
}
......@@ -230,22 +209,32 @@ static int vic_init(struct host1x_client *client)
static int vic_exit(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct iommu_group *group = iommu_group_get(client->dev);
struct drm_device *dev = dev_get_drvdata(client->parent);
struct tegra_drm *tegra = dev->dev_private;
struct vic *vic = to_vic(drm);
int err;
/* avoid a dangling pointer just in case this disappears */
client->dev->dma_parms = NULL;
err = tegra_drm_unregister_client(tegra, drm);
if (err < 0)
return err;
host1x_syncpt_free(client->syncpts[0]);
host1x_channel_put(vic->channel);
if (vic->domain) {
iommu_detach_group(vic->domain, group);
vic->domain = NULL;
host1x_client_iommu_detach(client);
if (client->group) {
dma_unmap_single(vic->dev, vic->falcon.firmware.phys,
vic->falcon.firmware.size, DMA_TO_DEVICE);
tegra_drm_free(tegra, vic->falcon.firmware.size,
vic->falcon.firmware.virt,
vic->falcon.firmware.iova);
} else {
dma_free_coherent(vic->dev, vic->falcon.firmware.size,
vic->falcon.firmware.virt,
vic->falcon.firmware.iova);
}
return 0;
......@@ -258,25 +247,64 @@ static const struct host1x_client_ops vic_client_ops = {
static int vic_load_firmware(struct vic *vic)
{
struct host1x_client *client = &vic->client.base;
struct tegra_drm *tegra = vic->client.drm;
dma_addr_t iova;
size_t size;
void *virt;
int err;
if (vic->falcon.data)
if (vic->falcon.firmware.virt)
return 0;
vic->falcon.data = vic->client.drm;
err = falcon_read_firmware(&vic->falcon, vic->config->firmware);
if (err < 0)
goto cleanup;
return err;
size = vic->falcon.firmware.size;
if (!client->group) {
virt = dma_alloc_coherent(vic->dev, size, &iova, GFP_KERNEL);
err = dma_mapping_error(vic->dev, iova);
if (err < 0)
return err;
} else {
virt = tegra_drm_alloc(tegra, size, &iova);
}
vic->falcon.firmware.virt = virt;
vic->falcon.firmware.iova = iova;
err = falcon_load_firmware(&vic->falcon);
if (err < 0)
goto cleanup;
/*
* In this case we have received an IOVA from the shared domain, so we
* need to make sure to get the physical address so that the DMA API
* knows what memory pages to flush the cache for.
*/
if (client->group) {
dma_addr_t phys;
phys = dma_map_single(vic->dev, virt, size, DMA_TO_DEVICE);
err = dma_mapping_error(vic->dev, phys);
if (err < 0)
goto cleanup;
vic->falcon.firmware.phys = phys;
}
return 0;
cleanup:
vic->falcon.data = NULL;
if (!client->group)
dma_free_coherent(vic->dev, size, virt, iova);
else
tegra_drm_free(tegra, size, virt, iova);
return err;
}
......@@ -374,6 +402,13 @@ static int vic_probe(struct platform_device *pdev)
struct vic *vic;
int err;
/* inherit DMA mask from host1x parent */
err = dma_coerce_mask_and_coherent(dev, *dev->parent->dma_mask);
if (err < 0) {
dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
return err;
}
vic = devm_kzalloc(dev, sizeof(*vic), GFP_KERNEL);
if (!vic)
return -ENOMEM;
......@@ -410,7 +445,6 @@ static int vic_probe(struct platform_device *pdev)
vic->falcon.dev = dev;
vic->falcon.regs = vic->regs;
vic->falcon.ops = &vic_falcon_ops;
err = falcon_init(&vic->falcon);
if (err < 0)
......
......@@ -2,7 +2,7 @@
config TEGRA_HOST1X
tristate "NVIDIA Tegra host1x driver"
depends on ARCH_TEGRA || (ARM && COMPILE_TEST)
select IOMMU_IOVA if IOMMU_SUPPORT
select IOMMU_IOVA
help
Driver for the NVIDIA Tegra host1x hardware.
......
......@@ -445,7 +445,7 @@ static int host1x_device_add(struct host1x *host1x,
of_dma_configure(&device->dev, host1x->dev->of_node, true);
device->dev.dma_parms = &device->dma_parms;
dma_set_max_seg_size(&device->dev, SZ_4M);
dma_set_max_seg_size(&device->dev, UINT_MAX);
err = host1x_device_parse_dt(device, driver);
if (err < 0) {
......
......@@ -232,9 +232,9 @@ unsigned int host1x_cdma_wait_locked(struct host1x_cdma *cdma,
*
* Must be called with the cdma lock held.
*/
int host1x_cdma_wait_pushbuffer_space(struct host1x *host1x,
struct host1x_cdma *cdma,
unsigned int needed)
static 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;
......
......@@ -115,14 +115,14 @@ static struct host1x_channel *acquire_unused_channel(struct host1x *host)
/**
* host1x_channel_request() - Allocate a channel
* @device: Host1x unit this channel will be used to send commands to
* @client: Host1x client this channel will be used to send commands to
*
* Allocates a new host1x channel for @device. May return NULL if CDMA
* Allocates a new host1x channel for @client. May return NULL if CDMA
* initialization fails.
*/
struct host1x_channel *host1x_channel_request(struct device *dev)
struct host1x_channel *host1x_channel_request(struct host1x_client *client)
{
struct host1x *host = dev_get_drvdata(dev->parent);
struct host1x *host = dev_get_drvdata(client->dev->parent);
struct host1x_channel_list *chlist = &host->channel_list;
struct host1x_channel *channel;
int err;
......@@ -133,7 +133,8 @@ struct host1x_channel *host1x_channel_request(struct device *dev)
kref_init(&channel->refcount);
mutex_init(&channel->submitlock);
channel->dev = dev;
channel->client = client;
channel->dev = client->dev;
err = host1x_hw_channel_init(host, channel, channel->id);
if (err < 0)
......@@ -148,7 +149,7 @@ struct host1x_channel *host1x_channel_request(struct device *dev)
fail:
clear_bit(channel->id, chlist->allocated_channels);
dev_err(dev, "failed to initialize channel\n");
dev_err(client->dev, "failed to initialize channel\n");
return NULL;
}
......
......@@ -26,6 +26,7 @@ struct host1x_channel {
unsigned int id;
struct mutex submitlock;
void __iomem *regs;
struct host1x_client *client;
struct device *dev;
struct host1x_cdma cdma;
};
......
......@@ -18,10 +18,6 @@
#include <trace/events/host1x.h>
#undef CREATE_TRACE_POINTS
#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
#include <asm/dma-iommu.h>
#endif
#include "bus.h"
#include "channel.h"
#include "debug.h"
......@@ -77,6 +73,10 @@ static const struct host1x_info host1x01_info = {
.init = host1x01_init,
.sync_offset = 0x3000,
.dma_mask = DMA_BIT_MASK(32),
.has_wide_gather = false,
.has_hypervisor = false,
.num_sid_entries = 0,
.sid_table = NULL,
};
static const struct host1x_info host1x02_info = {
......@@ -87,6 +87,10 @@ static const struct host1x_info host1x02_info = {
.init = host1x02_init,
.sync_offset = 0x3000,
.dma_mask = DMA_BIT_MASK(32),
.has_wide_gather = false,
.has_hypervisor = false,
.num_sid_entries = 0,
.sid_table = NULL,
};
static const struct host1x_info host1x04_info = {
......@@ -97,6 +101,10 @@ static const struct host1x_info host1x04_info = {
.init = host1x04_init,
.sync_offset = 0x2100,
.dma_mask = DMA_BIT_MASK(34),
.has_wide_gather = false,
.has_hypervisor = false,
.num_sid_entries = 0,
.sid_table = NULL,
};
static const struct host1x_info host1x05_info = {
......@@ -107,6 +115,10 @@ static const struct host1x_info host1x05_info = {
.init = host1x05_init,
.sync_offset = 0x2100,
.dma_mask = DMA_BIT_MASK(34),
.has_wide_gather = false,
.has_hypervisor = false,
.num_sid_entries = 0,
.sid_table = NULL,
};
static const struct host1x_sid_entry tegra186_sid_table[] = {
......@@ -126,6 +138,7 @@ static const struct host1x_info host1x06_info = {
.init = host1x06_init,
.sync_offset = 0x0,
.dma_mask = DMA_BIT_MASK(40),
.has_wide_gather = true,
.has_hypervisor = true,
.num_sid_entries = ARRAY_SIZE(tegra186_sid_table),
.sid_table = tegra186_sid_table,
......@@ -148,6 +161,7 @@ static const struct host1x_info host1x07_info = {
.init = host1x07_init,
.sync_offset = 0x0,
.dma_mask = DMA_BIT_MASK(40),
.has_wide_gather = true,
.has_hypervisor = true,
.num_sid_entries = ARRAY_SIZE(tegra194_sid_table),
.sid_table = tegra194_sid_table,
......@@ -178,6 +192,117 @@ static void host1x_setup_sid_table(struct host1x *host)
}
}
static struct iommu_domain *host1x_iommu_attach(struct host1x *host)
{
struct iommu_domain *domain = iommu_get_domain_for_dev(host->dev);
int err;
/*
* If the host1x firewall is enabled, there's no need to enable IOMMU
* support. Similarly, if host1x is already attached to an IOMMU (via
* the DMA API), don't try to attach again.
*/
if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) || domain)
return domain;
host->group = iommu_group_get(host->dev);
if (host->group) {
struct iommu_domain_geometry *geometry;
dma_addr_t start, end;
unsigned long order;
err = iova_cache_get();
if (err < 0)
goto put_group;
host->domain = iommu_domain_alloc(&platform_bus_type);
if (!host->domain) {
err = -ENOMEM;
goto put_cache;
}
err = iommu_attach_group(host->domain, host->group);
if (err) {
if (err == -ENODEV)
err = 0;
goto free_domain;
}
geometry = &host->domain->geometry;
start = geometry->aperture_start & host->info->dma_mask;
end = geometry->aperture_end & host->info->dma_mask;
order = __ffs(host->domain->pgsize_bitmap);
init_iova_domain(&host->iova, 1UL << order, start >> order);
host->iova_end = end;
domain = host->domain;
}
return domain;
free_domain:
iommu_domain_free(host->domain);
host->domain = NULL;
put_cache:
iova_cache_put();
put_group:
iommu_group_put(host->group);
host->group = NULL;
return ERR_PTR(err);
}
static int host1x_iommu_init(struct host1x *host)
{
u64 mask = host->info->dma_mask;
struct iommu_domain *domain;
int err;
domain = host1x_iommu_attach(host);
if (IS_ERR(domain)) {
err = PTR_ERR(domain);
dev_err(host->dev, "failed to attach to IOMMU: %d\n", err);
return err;
}
/*
* If we're not behind an IOMMU make sure we don't get push buffers
* that are allocated outside of the range addressable by the GATHER
* opcode.
*
* Newer generations of Tegra (Tegra186 and later) support a wide
* variant of the GATHER opcode that allows addressing more bits.
*/
if (!domain && !host->info->has_wide_gather)
mask = DMA_BIT_MASK(32);
err = dma_coerce_mask_and_coherent(host->dev, mask);
if (err < 0) {
dev_err(host->dev, "failed to set DMA mask: %d\n", err);
return err;
}
return 0;
}
static void host1x_iommu_exit(struct host1x *host)
{
if (host->domain) {
put_iova_domain(&host->iova);
iommu_detach_group(host->domain, host->group);
iommu_domain_free(host->domain);
host->domain = NULL;
iova_cache_put();
iommu_group_put(host->group);
host->group = NULL;
}
}
static int host1x_probe(struct platform_device *pdev)
{
struct host1x *host;
......@@ -237,7 +362,8 @@ static int host1x_probe(struct platform_device *pdev)
return PTR_ERR(host->hv_regs);
}
dma_set_mask_and_coherent(host->dev, host->info->dma_mask);
host->dev->dma_parms = &host->dma_parms;
dma_set_max_seg_size(host->dev, UINT_MAX);
if (host->info->init) {
err = host->info->init(host);
......@@ -261,87 +387,42 @@ static int host1x_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get reset: %d\n", err);
return err;
}
#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
if (host->dev->archdata.mapping) {
struct dma_iommu_mapping *mapping =
to_dma_iommu_mapping(host->dev);
arm_iommu_detach_device(host->dev);
arm_iommu_release_mapping(mapping);
}
#endif
if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL))
goto skip_iommu;
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();
if (err < 0)
goto put_group;
host->domain = iommu_domain_alloc(&platform_bus_type);
if (!host->domain) {
err = -ENOMEM;
goto put_cache;
}
err = iommu_attach_group(host->domain, host->group);
if (err) {
if (err == -ENODEV) {
iommu_domain_free(host->domain);
host->domain = NULL;
iova_cache_put();
iommu_group_put(host->group);
host->group = NULL;
goto skip_iommu;
}
goto fail_free_domain;
}
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, start >> order);
host->iova_end = end;
err = host1x_iommu_init(host);
if (err < 0) {
dev_err(&pdev->dev, "failed to setup IOMMU: %d\n", err);
return err;
}
skip_iommu:
err = host1x_channel_list_init(&host->channel_list,
host->info->nb_channels);
if (err) {
dev_err(&pdev->dev, "failed to initialize channel list\n");
goto fail_detach_device;
goto iommu_exit;
}
err = clk_prepare_enable(host->clk);
if (err < 0) {
dev_err(&pdev->dev, "failed to enable clock\n");
goto fail_free_channels;
goto free_channels;
}
err = reset_control_deassert(host->rst);
if (err < 0) {
dev_err(&pdev->dev, "failed to deassert reset: %d\n", err);
goto fail_unprepare_disable;
goto unprepare_disable;
}
err = host1x_syncpt_init(host);
if (err) {
dev_err(&pdev->dev, "failed to initialize syncpts\n");
goto fail_reset_assert;
goto reset_assert;
}
err = host1x_intr_init(host, syncpt_irq);
if (err) {
dev_err(&pdev->dev, "failed to initialize interrupts\n");
goto fail_deinit_syncpt;
goto deinit_syncpt;
}
host1x_debug_init(host);
......@@ -351,33 +432,22 @@ static int host1x_probe(struct platform_device *pdev)
err = host1x_register(host);
if (err < 0)
goto fail_deinit_intr;
goto deinit_intr;
return 0;
fail_deinit_intr:
deinit_intr:
host1x_intr_deinit(host);
fail_deinit_syncpt:
deinit_syncpt:
host1x_syncpt_deinit(host);
fail_reset_assert:
reset_assert:
reset_control_assert(host->rst);
fail_unprepare_disable:
unprepare_disable:
clk_disable_unprepare(host->clk);
fail_free_channels:
free_channels:
host1x_channel_list_free(&host->channel_list);
fail_detach_device:
if (host->group && host->domain) {
put_iova_domain(&host->iova);
iommu_detach_group(host->domain, host->group);
}
fail_free_domain:
if (host->domain)
iommu_domain_free(host->domain);
put_cache:
if (host->group)
iova_cache_put();
put_group:
iommu_group_put(host->group);
iommu_exit:
host1x_iommu_exit(host);
return err;
}
......@@ -387,18 +457,12 @@ static int host1x_remove(struct platform_device *pdev)
struct host1x *host = platform_get_drvdata(pdev);
host1x_unregister(host);
host1x_debug_deinit(host);
host1x_intr_deinit(host);
host1x_syncpt_deinit(host);
reset_control_assert(host->rst);
clk_disable_unprepare(host->clk);
if (host->domain) {
put_iova_domain(&host->iova);
iommu_detach_group(host->domain, host->group);
iommu_domain_free(host->domain);
iova_cache_put();
iommu_group_put(host->group);
}
host1x_iommu_exit(host);
return 0;
}
......
......@@ -97,6 +97,7 @@ struct host1x_info {
int (*init)(struct host1x *host1x); /* initialize per SoC ops */
unsigned int sync_offset; /* offset of syncpoint registers */
u64 dma_mask; /* mask of addressable memory */
bool has_wide_gather; /* supports GATHER_W opcode */
bool has_hypervisor; /* has hypervisor registers */
unsigned int num_sid_entries;
const struct host1x_sid_entry *sid_table;
......@@ -140,6 +141,8 @@ struct host1x {
struct list_head devices;
struct list_head list;
struct device_dma_parameters dma_parms;
};
void host1x_hypervisor_writel(struct host1x *host1x, u32 r, u32 v);
......
......@@ -105,7 +105,6 @@ static void action_submit_complete(struct host1x_waitlist *waiter)
/* Add nr_completed to trace */
trace_host1x_channel_submit_complete(dev_name(channel->dev),
waiter->count, waiter->thresh);
}
static void action_wakeup(struct host1x_waitlist *waiter)
......
......@@ -99,6 +99,8 @@ EXPORT_SYMBOL(host1x_job_add_gather);
static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
{
struct host1x_client *client = job->client;
struct device *dev = client->dev;
unsigned int i;
int err;
......@@ -106,8 +108,8 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
for (i = 0; i < job->num_relocs; i++) {
struct host1x_reloc *reloc = &job->relocs[i];
dma_addr_t phys_addr, *phys;
struct sg_table *sgt;
dma_addr_t phys_addr;
reloc->target.bo = host1x_bo_get(reloc->target.bo);
if (!reloc->target.bo) {
......@@ -115,7 +117,50 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto unpin;
}
phys_addr = host1x_bo_pin(reloc->target.bo, &sgt);
if (client->group)
phys = &phys_addr;
else
phys = NULL;
sgt = host1x_bo_pin(dev, reloc->target.bo, phys);
if (IS_ERR(sgt)) {
err = PTR_ERR(sgt);
goto unpin;
}
if (sgt) {
unsigned long mask = HOST1X_RELOC_READ |
HOST1X_RELOC_WRITE;
enum dma_data_direction dir;
switch (reloc->flags & mask) {
case HOST1X_RELOC_READ:
dir = DMA_TO_DEVICE;
break;
case HOST1X_RELOC_WRITE:
dir = DMA_FROM_DEVICE;
break;
case HOST1X_RELOC_READ | HOST1X_RELOC_WRITE:
dir = DMA_BIDIRECTIONAL;
break;
default:
err = -EINVAL;
goto unpin;
}
err = dma_map_sg(dev, sgt->sgl, sgt->nents, dir);
if (!err) {
err = -ENOMEM;
goto unpin;
}
job->unpins[job->num_unpins].dev = dev;
job->unpins[job->num_unpins].dir = dir;
phys_addr = sg_dma_address(sgt->sgl);
}
job->addr_phys[job->num_unpins] = phys_addr;
job->unpins[job->num_unpins].bo = reloc->target.bo;
......@@ -139,7 +184,11 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto unpin;
}
phys_addr = host1x_bo_pin(g->bo, &sgt);
sgt = host1x_bo_pin(host->dev, g->bo, NULL);
if (IS_ERR(sgt)) {
err = PTR_ERR(sgt);
goto unpin;
}
if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && host->domain) {
for_each_sg(sgt->sgl, sg, sgt->nents, j)
......@@ -163,15 +212,24 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto unpin;
}
job->addr_phys[job->num_unpins] =
iova_dma_addr(&host->iova, alloc);
job->unpins[job->num_unpins].size = gather_size;
phys_addr = iova_dma_addr(&host->iova, alloc);
} else {
job->addr_phys[job->num_unpins] = phys_addr;
err = dma_map_sg(host->dev, sgt->sgl, sgt->nents,
DMA_TO_DEVICE);
if (!err) {
err = -ENOMEM;
goto unpin;
}
job->unpins[job->num_unpins].dev = host->dev;
phys_addr = sg_dma_address(sgt->sgl);
}
job->gather_addr_phys[i] = job->addr_phys[job->num_unpins];
job->addr_phys[job->num_unpins] = phys_addr;
job->gather_addr_phys[i] = phys_addr;
job->unpins[job->num_unpins].dir = DMA_TO_DEVICE;
job->unpins[job->num_unpins].bo = g->bo;
job->unpins[job->num_unpins].sgt = sgt;
job->num_unpins++;
......@@ -436,7 +494,8 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g)
return err;
}
static inline int copy_gathers(struct host1x_job *job, struct device *dev)
static inline int copy_gathers(struct device *host, struct host1x_job *job,
struct device *dev)
{
struct host1x_firewall fw;
size_t size = 0;
......@@ -459,12 +518,12 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev)
* Try a non-blocking allocation from a higher priority pools first,
* as awaiting for the allocation here is a major performance hit.
*/
job->gather_copy_mapped = dma_alloc_wc(dev, size, &job->gather_copy,
job->gather_copy_mapped = dma_alloc_wc(host, size, &job->gather_copy,
GFP_NOWAIT);
/* the higher priority allocation failed, try the generic-blocking */
if (!job->gather_copy_mapped)
job->gather_copy_mapped = dma_alloc_wc(dev, size,
job->gather_copy_mapped = dma_alloc_wc(host, size,
&job->gather_copy,
GFP_KERNEL);
if (!job->gather_copy_mapped)
......@@ -512,7 +571,7 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev)
goto out;
if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) {
err = copy_gathers(job, dev);
err = copy_gathers(host->dev, job, dev);
if (err)
goto out;
}
......@@ -557,6 +616,8 @@ void host1x_job_unpin(struct host1x_job *job)
for (i = 0; i < job->num_unpins; i++) {
struct host1x_job_unpin_data *unpin = &job->unpins[i];
struct device *dev = unpin->dev ?: host->dev;
struct sg_table *sgt = unpin->sgt;
if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) &&
unpin->size && host->domain) {
......@@ -566,14 +627,18 @@ void host1x_job_unpin(struct host1x_job *job)
iova_pfn(&host->iova, job->addr_phys[i]));
}
host1x_bo_unpin(unpin->bo, unpin->sgt);
if (unpin->dev && sgt)
dma_unmap_sg(unpin->dev, sgt->sgl, sgt->nents,
unpin->dir);
host1x_bo_unpin(dev, unpin->bo, sgt);
host1x_bo_put(unpin->bo);
}
job->num_unpins = 0;
if (job->gather_copy_size)
dma_free_wc(job->channel->dev, job->gather_copy_size,
dma_free_wc(host->dev, job->gather_copy_size,
job->gather_copy_mapped, job->gather_copy);
}
EXPORT_SYMBOL(host1x_job_unpin);
......
......@@ -8,6 +8,8 @@
#ifndef __HOST1X_JOB_H
#define __HOST1X_JOB_H
#include <linux/dma-direction.h>
struct host1x_job_gather {
unsigned int words;
dma_addr_t base;
......@@ -19,7 +21,9 @@ struct host1x_job_gather {
struct host1x_job_unpin_data {
struct host1x_bo *bo;
struct sg_table *sgt;
struct device *dev;
size_t size;
enum dma_data_direction dir;
};
/*
......
......@@ -18,6 +18,7 @@ enum host1x_class {
};
struct host1x_client;
struct iommu_group;
/**
* struct host1x_client_ops - host1x client operations
......@@ -34,6 +35,7 @@ struct host1x_client_ops {
* @list: list node for the host1x client
* @parent: pointer to struct device representing the host1x controller
* @dev: pointer to struct device backing this host1x client
* @group: IOMMU group that this client is a member of
* @ops: host1x client operations
* @class: host1x class represented by this client
* @channel: host1x channel associated with this client
......@@ -44,6 +46,7 @@ struct host1x_client {
struct list_head list;
struct device *parent;
struct device *dev;
struct iommu_group *group;
const struct host1x_client_ops *ops;
......@@ -64,8 +67,9 @@ struct sg_table;
struct host1x_bo_ops {
struct host1x_bo *(*get)(struct host1x_bo *bo);
void (*put)(struct host1x_bo *bo);
dma_addr_t (*pin)(struct host1x_bo *bo, struct sg_table **sgt);
void (*unpin)(struct host1x_bo *bo, struct sg_table *sgt);
struct sg_table *(*pin)(struct device *dev, struct host1x_bo *bo,
dma_addr_t *phys);
void (*unpin)(struct device *dev, struct sg_table *sgt);
void *(*mmap)(struct host1x_bo *bo);
void (*munmap)(struct host1x_bo *bo, void *addr);
void *(*kmap)(struct host1x_bo *bo, unsigned int pagenum);
......@@ -92,15 +96,17 @@ static inline void host1x_bo_put(struct host1x_bo *bo)
bo->ops->put(bo);
}
static inline dma_addr_t host1x_bo_pin(struct host1x_bo *bo,
struct sg_table **sgt)
static inline struct sg_table *host1x_bo_pin(struct device *dev,
struct host1x_bo *bo,
dma_addr_t *phys)
{
return bo->ops->pin(bo, sgt);
return bo->ops->pin(dev, bo, phys);
}
static inline void host1x_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
static inline void host1x_bo_unpin(struct device *dev, struct host1x_bo *bo,
struct sg_table *sgt)
{
bo->ops->unpin(bo, sgt);
bo->ops->unpin(dev, sgt);
}
static inline void *host1x_bo_mmap(struct host1x_bo *bo)
......@@ -158,7 +164,7 @@ u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base);
struct host1x_channel;
struct host1x_job;
struct host1x_channel *host1x_channel_request(struct device *dev);
struct host1x_channel *host1x_channel_request(struct host1x_client *client);
struct host1x_channel *host1x_channel_get(struct host1x_channel *channel);
void host1x_channel_put(struct host1x_channel *channel);
int host1x_job_submit(struct host1x_job *job);
......@@ -167,6 +173,9 @@ int host1x_job_submit(struct host1x_job *job);
* host1x job
*/
#define HOST1X_RELOC_READ (1 << 0)
#define HOST1X_RELOC_WRITE (1 << 1)
struct host1x_reloc {
struct {
struct host1x_bo *bo;
......@@ -177,6 +186,7 @@ struct host1x_reloc {
unsigned long offset;
} target;
unsigned long shift;
unsigned long flags;
};
struct host1x_job {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment