Merge drm-upstream/drm-next into drm-misc-next
We got a few conflicts in drm_atomic.c after merging the DRM writeback support, now we need a backmerge to unlock develop development on drm-misc-next. Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.com>
This commit is contained in:
@@ -163,28 +163,89 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
|
||||
BLEND_COLOR_KEY_NONE;
|
||||
u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
|
||||
struct tegra_plane_state *state;
|
||||
u32 blending[2];
|
||||
unsigned int i;
|
||||
|
||||
state = to_tegra_plane_state(plane->base.state);
|
||||
|
||||
/* alpha contribution is 1 minus sum of overlapping windows */
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (state->dependent[i])
|
||||
background[i] |= BLEND_CONTROL_DEPENDENT;
|
||||
}
|
||||
|
||||
/* enable alpha blending if pixel format has an alpha component */
|
||||
if (!state->opaque)
|
||||
foreground |= BLEND_CONTROL_ALPHA;
|
||||
|
||||
/*
|
||||
* Disable blending and assume Window A is the bottom-most window,
|
||||
* Window C is the top-most window and Window B is in the middle.
|
||||
*/
|
||||
/* disable blending for non-overlapping case */
|
||||
tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
|
||||
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);
|
||||
|
||||
switch (plane->index) {
|
||||
state = to_tegra_plane_state(plane->base.state);
|
||||
|
||||
if (state->opaque) {
|
||||
/*
|
||||
* Since custom fix-weight blending isn't utilized and weight
|
||||
* of top window is set to max, we can enforce dependent
|
||||
* blending which in this case results in transparent bottom
|
||||
* window if top window is opaque and if top window enables
|
||||
* alpha blending, then bottom window is getting alpha value
|
||||
* of 1 minus the sum of alpha components of the overlapping
|
||||
* plane.
|
||||
*/
|
||||
background[0] |= BLEND_CONTROL_DEPENDENT;
|
||||
background[1] |= BLEND_CONTROL_DEPENDENT;
|
||||
|
||||
/*
|
||||
* The region where three windows overlap is the intersection
|
||||
* of the two regions where two windows overlap. It contributes
|
||||
* to the area if all of the windows on top of it have an alpha
|
||||
* component.
|
||||
*/
|
||||
switch (state->base.normalized_zpos) {
|
||||
case 0:
|
||||
if (state->blending[0].alpha &&
|
||||
state->blending[1].alpha)
|
||||
background[2] |= BLEND_CONTROL_DEPENDENT;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
background[2] |= BLEND_CONTROL_DEPENDENT;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Enable alpha blending if pixel format has an alpha
|
||||
* component.
|
||||
*/
|
||||
foreground |= BLEND_CONTROL_ALPHA;
|
||||
|
||||
/*
|
||||
* If any of the windows on top of this window is opaque, it
|
||||
* will completely conceal this window within that area. If
|
||||
* top window has an alpha component, it is blended over the
|
||||
* bottom window.
|
||||
*/
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (state->blending[i].alpha &&
|
||||
state->blending[i].top)
|
||||
background[i] |= BLEND_CONTROL_DEPENDENT;
|
||||
}
|
||||
|
||||
switch (state->base.normalized_zpos) {
|
||||
case 0:
|
||||
if (state->blending[0].alpha &&
|
||||
state->blending[1].alpha)
|
||||
background[2] |= BLEND_CONTROL_DEPENDENT;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/*
|
||||
* When both middle and topmost windows have an alpha,
|
||||
* these windows a mixed together and then the result
|
||||
* is blended over the bottom window.
|
||||
*/
|
||||
if (state->blending[0].alpha &&
|
||||
state->blending[0].top)
|
||||
background[2] |= BLEND_CONTROL_ALPHA;
|
||||
|
||||
if (state->blending[1].alpha &&
|
||||
state->blending[1].top)
|
||||
background[2] |= BLEND_CONTROL_ALPHA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (state->base.normalized_zpos) {
|
||||
case 0:
|
||||
tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X);
|
||||
tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
|
||||
@@ -192,8 +253,21 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
|
||||
break;
|
||||
|
||||
case 1:
|
||||
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
|
||||
tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
|
||||
/*
|
||||
* If window B / C is topmost, then X / Y registers are
|
||||
* matching the order of blending[...] state indices,
|
||||
* otherwise a swap is required.
|
||||
*/
|
||||
if (!state->blending[0].top && state->blending[1].top) {
|
||||
blending[0] = foreground;
|
||||
blending[1] = background[1];
|
||||
} else {
|
||||
blending[0] = background[0];
|
||||
blending[1] = foreground;
|
||||
}
|
||||
|
||||
tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X);
|
||||
tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y);
|
||||
tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
|
||||
break;
|
||||
|
||||
@@ -224,6 +298,39 @@ static void tegra_plane_setup_blending(struct tegra_plane *plane,
|
||||
tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL);
|
||||
}
|
||||
|
||||
static bool
|
||||
tegra_plane_use_horizontal_filtering(struct tegra_plane *plane,
|
||||
const struct tegra_dc_window *window)
|
||||
{
|
||||
struct tegra_dc *dc = plane->dc;
|
||||
|
||||
if (window->src.w == window->dst.w)
|
||||
return false;
|
||||
|
||||
if (plane->index == 0 && dc->soc->has_win_a_without_filters)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
tegra_plane_use_vertical_filtering(struct tegra_plane *plane,
|
||||
const struct tegra_dc_window *window)
|
||||
{
|
||||
struct tegra_dc *dc = plane->dc;
|
||||
|
||||
if (window->src.h == window->dst.h)
|
||||
return false;
|
||||
|
||||
if (plane->index == 0 && dc->soc->has_win_a_without_filters)
|
||||
return false;
|
||||
|
||||
if (plane->index == 2 && dc->soc->has_win_c_without_vert_filter)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void tegra_dc_setup_window(struct tegra_plane *plane,
|
||||
const struct tegra_dc_window *window)
|
||||
{
|
||||
@@ -361,12 +468,50 @@ static void tegra_dc_setup_window(struct tegra_plane *plane,
|
||||
if (window->bottom_up)
|
||||
value |= V_DIRECTION;
|
||||
|
||||
if (tegra_plane_use_horizontal_filtering(plane, window)) {
|
||||
/*
|
||||
* Enable horizontal 6-tap filter and set filtering
|
||||
* coefficients to the default values defined in TRM.
|
||||
*/
|
||||
tegra_plane_writel(plane, 0x00008000, DC_WIN_H_FILTER_P(0));
|
||||
tegra_plane_writel(plane, 0x3e087ce1, DC_WIN_H_FILTER_P(1));
|
||||
tegra_plane_writel(plane, 0x3b117ac1, DC_WIN_H_FILTER_P(2));
|
||||
tegra_plane_writel(plane, 0x591b73aa, DC_WIN_H_FILTER_P(3));
|
||||
tegra_plane_writel(plane, 0x57256d9a, DC_WIN_H_FILTER_P(4));
|
||||
tegra_plane_writel(plane, 0x552f668b, DC_WIN_H_FILTER_P(5));
|
||||
tegra_plane_writel(plane, 0x73385e8b, DC_WIN_H_FILTER_P(6));
|
||||
tegra_plane_writel(plane, 0x72435583, DC_WIN_H_FILTER_P(7));
|
||||
tegra_plane_writel(plane, 0x714c4c8b, DC_WIN_H_FILTER_P(8));
|
||||
tegra_plane_writel(plane, 0x70554393, DC_WIN_H_FILTER_P(9));
|
||||
tegra_plane_writel(plane, 0x715e389b, DC_WIN_H_FILTER_P(10));
|
||||
tegra_plane_writel(plane, 0x71662faa, DC_WIN_H_FILTER_P(11));
|
||||
tegra_plane_writel(plane, 0x536d25ba, DC_WIN_H_FILTER_P(12));
|
||||
tegra_plane_writel(plane, 0x55731bca, DC_WIN_H_FILTER_P(13));
|
||||
tegra_plane_writel(plane, 0x387a11d9, DC_WIN_H_FILTER_P(14));
|
||||
tegra_plane_writel(plane, 0x3c7c08f1, DC_WIN_H_FILTER_P(15));
|
||||
|
||||
value |= H_FILTER;
|
||||
}
|
||||
|
||||
if (tegra_plane_use_vertical_filtering(plane, window)) {
|
||||
unsigned int i, k;
|
||||
|
||||
/*
|
||||
* Enable vertical 2-tap filter and set filtering
|
||||
* coefficients to the default values defined in TRM.
|
||||
*/
|
||||
for (i = 0, k = 128; i < 16; i++, k -= 8)
|
||||
tegra_plane_writel(plane, k, DC_WIN_V_FILTER_P(i));
|
||||
|
||||
value |= V_FILTER;
|
||||
}
|
||||
|
||||
tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS);
|
||||
|
||||
if (dc->soc->supports_blending)
|
||||
tegra_plane_setup_blending(plane, window);
|
||||
else
|
||||
if (dc->soc->has_legacy_blending)
|
||||
tegra_plane_setup_blending_legacy(plane);
|
||||
else
|
||||
tegra_plane_setup_blending(plane, window);
|
||||
}
|
||||
|
||||
static const u32 tegra20_primary_formats[] = {
|
||||
@@ -451,17 +596,18 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct tegra_plane_state *plane_state = to_tegra_plane_state(state);
|
||||
unsigned int rotation = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_Y;
|
||||
struct tegra_bo_tiling *tiling = &plane_state->tiling;
|
||||
struct tegra_plane *tegra = to_tegra_plane(plane);
|
||||
struct tegra_dc *dc = to_tegra_dc(state->crtc);
|
||||
unsigned int format;
|
||||
int err;
|
||||
|
||||
/* no need for further checks if the plane is being disabled */
|
||||
if (!state->crtc)
|
||||
return 0;
|
||||
|
||||
err = tegra_plane_format(state->fb->format->format, &format,
|
||||
err = tegra_plane_format(state->fb->format->format,
|
||||
&plane_state->format,
|
||||
&plane_state->swap);
|
||||
if (err < 0)
|
||||
return err;
|
||||
@@ -472,22 +618,12 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
|
||||
* the corresponding opaque formats. However, the opaque formats can
|
||||
* be emulated by disabling alpha blending for the plane.
|
||||
*/
|
||||
if (!dc->soc->supports_blending) {
|
||||
if (!tegra_plane_format_has_alpha(format)) {
|
||||
err = tegra_plane_format_get_alpha(format, &format);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
plane_state->opaque = true;
|
||||
} else {
|
||||
plane_state->opaque = false;
|
||||
}
|
||||
|
||||
tegra_plane_check_dependent(tegra, plane_state);
|
||||
if (dc->soc->has_legacy_blending) {
|
||||
err = tegra_plane_setup_legacy_state(tegra, plane_state);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
plane_state->format = format;
|
||||
|
||||
err = tegra_fb_get_tiling(state->fb, tiling);
|
||||
if (err < 0)
|
||||
return err;
|
||||
@@ -498,6 +634,13 @@ static int tegra_plane_atomic_check(struct drm_plane *plane,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rotation = drm_rotation_simplify(state->rotation, rotation);
|
||||
|
||||
if (rotation & DRM_MODE_REFLECT_Y)
|
||||
plane_state->bottom_up = true;
|
||||
else
|
||||
plane_state->bottom_up = false;
|
||||
|
||||
/*
|
||||
* Tegra doesn't support different strides for U and V planes so we
|
||||
* error out if the user tries to display a framebuffer with such a
|
||||
@@ -558,7 +701,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane,
|
||||
window.dst.w = drm_rect_width(&plane->state->dst);
|
||||
window.dst.h = drm_rect_height(&plane->state->dst);
|
||||
window.bits_per_pixel = fb->format->cpp[0] * 8;
|
||||
window.bottom_up = tegra_fb_is_bottom_up(fb);
|
||||
window.bottom_up = tegra_fb_is_bottom_up(fb) || state->bottom_up;
|
||||
|
||||
/* copy from state */
|
||||
window.zpos = plane->state->normalized_zpos;
|
||||
@@ -639,9 +782,15 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
|
||||
}
|
||||
|
||||
drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
|
||||
drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
|
||||
|
||||
if (dc->soc->supports_blending)
|
||||
drm_plane_create_zpos_property(&plane->base, 0, 0, 255);
|
||||
err = drm_plane_create_rotation_property(&plane->base,
|
||||
DRM_MODE_ROTATE_0,
|
||||
DRM_MODE_ROTATE_0 |
|
||||
DRM_MODE_REFLECT_Y);
|
||||
if (err < 0)
|
||||
dev_err(dc->dev, "failed to create rotation property: %d\n",
|
||||
err);
|
||||
|
||||
return &plane->base;
|
||||
}
|
||||
@@ -918,9 +1067,15 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
|
||||
}
|
||||
|
||||
drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
|
||||
drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
|
||||
|
||||
if (dc->soc->supports_blending)
|
||||
drm_plane_create_zpos_property(&plane->base, 0, 0, 255);
|
||||
err = drm_plane_create_rotation_property(&plane->base,
|
||||
DRM_MODE_ROTATE_0,
|
||||
DRM_MODE_ROTATE_0 |
|
||||
DRM_MODE_REFLECT_Y);
|
||||
if (err < 0)
|
||||
dev_err(dc->dev, "failed to create rotation property: %d\n",
|
||||
err);
|
||||
|
||||
return &plane->base;
|
||||
}
|
||||
@@ -1826,7 +1981,6 @@ static irqreturn_t tegra_dc_irq(int irq, void *data)
|
||||
static int tegra_dc_init(struct host1x_client *client)
|
||||
{
|
||||
struct drm_device *drm = dev_get_drvdata(client->parent);
|
||||
struct iommu_group *group = iommu_group_get(client->dev);
|
||||
unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
|
||||
struct tegra_dc *dc = host1x_client_to_dc(client);
|
||||
struct tegra_drm *tegra = drm->dev_private;
|
||||
@@ -1838,20 +1992,11 @@ static int tegra_dc_init(struct host1x_client *client)
|
||||
if (!dc->syncpt)
|
||||
dev_warn(dc->dev, "failed to allocate syncpoint\n");
|
||||
|
||||
if (group && tegra->domain) {
|
||||
if (group != tegra->group) {
|
||||
err = iommu_attach_group(tegra->domain, group);
|
||||
if (err < 0) {
|
||||
dev_err(dc->dev,
|
||||
"failed to attach to domain: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
tegra->group = group;
|
||||
}
|
||||
|
||||
dc->domain = tegra->domain;
|
||||
dc->group = host1x_client_iommu_attach(client, true);
|
||||
if (IS_ERR(dc->group)) {
|
||||
err = PTR_ERR(dc->group);
|
||||
dev_err(client->dev, "failed to attach to domain: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (dc->soc->wgrps)
|
||||
@@ -1916,24 +2061,15 @@ cleanup:
|
||||
if (!IS_ERR(primary))
|
||||
drm_plane_cleanup(primary);
|
||||
|
||||
if (group && dc->domain) {
|
||||
if (group == tegra->group) {
|
||||
iommu_detach_group(dc->domain, group);
|
||||
tegra->group = NULL;
|
||||
}
|
||||
|
||||
dc->domain = NULL;
|
||||
}
|
||||
host1x_client_iommu_detach(client, dc->group);
|
||||
host1x_syncpt_free(dc->syncpt);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra_dc_exit(struct host1x_client *client)
|
||||
{
|
||||
struct drm_device *drm = dev_get_drvdata(client->parent);
|
||||
struct iommu_group *group = iommu_group_get(client->dev);
|
||||
struct tegra_dc *dc = host1x_client_to_dc(client);
|
||||
struct tegra_drm *tegra = drm->dev_private;
|
||||
int err;
|
||||
|
||||
devm_free_irq(dc->dev, dc->irq, dc);
|
||||
@@ -1944,15 +2080,7 @@ static int tegra_dc_exit(struct host1x_client *client)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (group && dc->domain) {
|
||||
if (group == tegra->group) {
|
||||
iommu_detach_group(dc->domain, group);
|
||||
tegra->group = NULL;
|
||||
}
|
||||
|
||||
dc->domain = NULL;
|
||||
}
|
||||
|
||||
host1x_client_iommu_detach(client, dc->group);
|
||||
host1x_syncpt_free(dc->syncpt);
|
||||
|
||||
return 0;
|
||||
@@ -1968,7 +2096,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
|
||||
.supports_interlacing = false,
|
||||
.supports_cursor = false,
|
||||
.supports_block_linear = false,
|
||||
.supports_blending = false,
|
||||
.has_legacy_blending = true,
|
||||
.pitch_align = 8,
|
||||
.has_powergate = false,
|
||||
.coupled_pm = true,
|
||||
@@ -1978,6 +2106,8 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
|
||||
.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
|
||||
.overlay_formats = tegra20_overlay_formats,
|
||||
.modifiers = tegra20_modifiers,
|
||||
.has_win_a_without_filters = true,
|
||||
.has_win_c_without_vert_filter = true,
|
||||
};
|
||||
|
||||
static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
|
||||
@@ -1985,7 +2115,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
|
||||
.supports_interlacing = false,
|
||||
.supports_cursor = false,
|
||||
.supports_block_linear = false,
|
||||
.supports_blending = false,
|
||||
.has_legacy_blending = true,
|
||||
.pitch_align = 8,
|
||||
.has_powergate = false,
|
||||
.coupled_pm = false,
|
||||
@@ -1995,6 +2125,8 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
|
||||
.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
|
||||
.overlay_formats = tegra20_overlay_formats,
|
||||
.modifiers = tegra20_modifiers,
|
||||
.has_win_a_without_filters = false,
|
||||
.has_win_c_without_vert_filter = false,
|
||||
};
|
||||
|
||||
static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
|
||||
@@ -2002,7 +2134,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
|
||||
.supports_interlacing = false,
|
||||
.supports_cursor = false,
|
||||
.supports_block_linear = false,
|
||||
.supports_blending = false,
|
||||
.has_legacy_blending = true,
|
||||
.pitch_align = 64,
|
||||
.has_powergate = true,
|
||||
.coupled_pm = false,
|
||||
@@ -2012,6 +2144,8 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
|
||||
.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
|
||||
.overlay_formats = tegra114_overlay_formats,
|
||||
.modifiers = tegra20_modifiers,
|
||||
.has_win_a_without_filters = false,
|
||||
.has_win_c_without_vert_filter = false,
|
||||
};
|
||||
|
||||
static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
|
||||
@@ -2019,7 +2153,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
|
||||
.supports_interlacing = true,
|
||||
.supports_cursor = true,
|
||||
.supports_block_linear = true,
|
||||
.supports_blending = true,
|
||||
.has_legacy_blending = false,
|
||||
.pitch_align = 64,
|
||||
.has_powergate = true,
|
||||
.coupled_pm = false,
|
||||
@@ -2029,6 +2163,8 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
|
||||
.num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats),
|
||||
.overlay_formats = tegra124_overlay_formats,
|
||||
.modifiers = tegra124_modifiers,
|
||||
.has_win_a_without_filters = false,
|
||||
.has_win_c_without_vert_filter = false,
|
||||
};
|
||||
|
||||
static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
|
||||
@@ -2036,7 +2172,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
|
||||
.supports_interlacing = true,
|
||||
.supports_cursor = true,
|
||||
.supports_block_linear = true,
|
||||
.supports_blending = true,
|
||||
.has_legacy_blending = false,
|
||||
.pitch_align = 64,
|
||||
.has_powergate = true,
|
||||
.coupled_pm = false,
|
||||
@@ -2046,6 +2182,8 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
|
||||
.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
|
||||
.overlay_formats = tegra114_overlay_formats,
|
||||
.modifiers = tegra124_modifiers,
|
||||
.has_win_a_without_filters = false,
|
||||
.has_win_c_without_vert_filter = false,
|
||||
};
|
||||
|
||||
static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = {
|
||||
@@ -2087,7 +2225,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
|
||||
.supports_interlacing = true,
|
||||
.supports_cursor = true,
|
||||
.supports_block_linear = true,
|
||||
.supports_blending = true,
|
||||
.has_legacy_blending = false,
|
||||
.pitch_align = 64,
|
||||
.has_powergate = false,
|
||||
.coupled_pm = false,
|
||||
|
@@ -55,7 +55,7 @@ struct tegra_dc_soc_info {
|
||||
bool supports_interlacing;
|
||||
bool supports_cursor;
|
||||
bool supports_block_linear;
|
||||
bool supports_blending;
|
||||
bool has_legacy_blending;
|
||||
unsigned int pitch_align;
|
||||
bool has_powergate;
|
||||
bool coupled_pm;
|
||||
@@ -67,6 +67,8 @@ struct tegra_dc_soc_info {
|
||||
const u32 *overlay_formats;
|
||||
unsigned int num_overlay_formats;
|
||||
const u64 *modifiers;
|
||||
bool has_win_a_without_filters;
|
||||
bool has_win_c_without_vert_filter;
|
||||
};
|
||||
|
||||
struct tegra_dc {
|
||||
@@ -92,7 +94,7 @@ struct tegra_dc {
|
||||
|
||||
const struct tegra_dc_soc_info *soc;
|
||||
|
||||
struct iommu_domain *domain;
|
||||
struct iommu_group *group;
|
||||
};
|
||||
|
||||
static inline struct tegra_dc *
|
||||
@@ -553,6 +555,9 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
|
||||
#define THREAD_NUM(x) (((x) & 0x1f) << 1)
|
||||
#define THREAD_GROUP_ENABLE (1 << 0)
|
||||
|
||||
#define DC_WIN_H_FILTER_P(p) (0x601 + (p))
|
||||
#define DC_WIN_V_FILTER_P(p) (0x619 + (p))
|
||||
|
||||
#define DC_WIN_CSC_YOF 0x611
|
||||
#define DC_WIN_CSC_KYRGB 0x612
|
||||
#define DC_WIN_CSC_KUR 0x613
|
||||
@@ -566,6 +571,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
|
||||
#define H_DIRECTION (1 << 0)
|
||||
#define V_DIRECTION (1 << 2)
|
||||
#define COLOR_EXPAND (1 << 6)
|
||||
#define H_FILTER (1 << 8)
|
||||
#define V_FILTER (1 << 10)
|
||||
#define CSC_ENABLE (1 << 18)
|
||||
#define WIN_ENABLE (1 << 30)
|
||||
|
||||
|
@@ -98,6 +98,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
|
||||
goto free;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -191,11 +195,14 @@ config:
|
||||
drm_mode_config_cleanup(drm);
|
||||
|
||||
if (tegra->domain) {
|
||||
iommu_domain_free(tegra->domain);
|
||||
drm_mm_takedown(&tegra->mm);
|
||||
mutex_destroy(&tegra->mm_lock);
|
||||
drm_mm_takedown(&tegra->mm);
|
||||
put_iova_domain(&tegra->carveout.domain);
|
||||
iova_cache_put();
|
||||
}
|
||||
domain:
|
||||
if (tegra->domain)
|
||||
iommu_domain_free(tegra->domain);
|
||||
free:
|
||||
kfree(tegra);
|
||||
return err;
|
||||
@@ -217,10 +224,11 @@ static void tegra_drm_unload(struct drm_device *drm)
|
||||
return;
|
||||
|
||||
if (tegra->domain) {
|
||||
iommu_domain_free(tegra->domain);
|
||||
drm_mm_takedown(&tegra->mm);
|
||||
mutex_destroy(&tegra->mm_lock);
|
||||
drm_mm_takedown(&tegra->mm);
|
||||
put_iova_domain(&tegra->carveout.domain);
|
||||
iova_cache_put();
|
||||
iommu_domain_free(tegra->domain);
|
||||
}
|
||||
|
||||
kfree(tegra);
|
||||
@@ -300,46 +308,15 @@ static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int host1x_waitchk_copy_from_user(struct host1x_waitchk *dest,
|
||||
struct drm_tegra_waitchk __user *src,
|
||||
struct drm_file *file)
|
||||
{
|
||||
u32 cmdbuf;
|
||||
int err;
|
||||
|
||||
err = get_user(cmdbuf, &src->handle);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = get_user(dest->offset, &src->offset);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = get_user(dest->syncpt_id, &src->syncpt);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = get_user(dest->thresh, &src->thresh);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
dest->bo = host1x_bo_lookup(file, cmdbuf);
|
||||
if (!dest->bo)
|
||||
return -ENOENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tegra_drm_submit(struct tegra_drm_context *context,
|
||||
struct drm_tegra_submit *args, struct drm_device *drm,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct host1x_client *client = &context->client->base;
|
||||
unsigned int num_cmdbufs = args->num_cmdbufs;
|
||||
unsigned int num_relocs = args->num_relocs;
|
||||
unsigned int num_waitchks = args->num_waitchks;
|
||||
struct drm_tegra_cmdbuf __user *user_cmdbufs;
|
||||
struct drm_tegra_reloc __user *user_relocs;
|
||||
struct drm_tegra_waitchk __user *user_waitchks;
|
||||
struct drm_tegra_syncpt __user *user_syncpt;
|
||||
struct drm_tegra_syncpt syncpt;
|
||||
struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
|
||||
@@ -351,7 +328,6 @@ int tegra_drm_submit(struct tegra_drm_context *context,
|
||||
|
||||
user_cmdbufs = u64_to_user_ptr(args->cmdbufs);
|
||||
user_relocs = u64_to_user_ptr(args->relocs);
|
||||
user_waitchks = u64_to_user_ptr(args->waitchks);
|
||||
user_syncpt = u64_to_user_ptr(args->syncpts);
|
||||
|
||||
/* We don't yet support other than one syncpt_incr struct per submit */
|
||||
@@ -363,21 +339,20 @@ int tegra_drm_submit(struct tegra_drm_context *context,
|
||||
return -EINVAL;
|
||||
|
||||
job = host1x_job_alloc(context->channel, args->num_cmdbufs,
|
||||
args->num_relocs, args->num_waitchks);
|
||||
args->num_relocs);
|
||||
if (!job)
|
||||
return -ENOMEM;
|
||||
|
||||
job->num_relocs = args->num_relocs;
|
||||
job->num_waitchk = args->num_waitchks;
|
||||
job->client = (u32)args->context;
|
||||
job->class = context->client->base.class;
|
||||
job->client = client;
|
||||
job->class = client->class;
|
||||
job->serialize = true;
|
||||
|
||||
/*
|
||||
* Track referenced BOs so that they can be unreferenced after the
|
||||
* submission is complete.
|
||||
*/
|
||||
num_refs = num_cmdbufs + num_relocs * 2 + num_waitchks;
|
||||
num_refs = num_cmdbufs + num_relocs * 2;
|
||||
|
||||
refs = kmalloc_array(num_refs, sizeof(*refs), GFP_KERNEL);
|
||||
if (!refs) {
|
||||
@@ -438,13 +413,13 @@ int tegra_drm_submit(struct tegra_drm_context *context,
|
||||
struct host1x_reloc *reloc;
|
||||
struct tegra_bo *obj;
|
||||
|
||||
err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs],
|
||||
err = host1x_reloc_copy_from_user(&job->relocs[num_relocs],
|
||||
&user_relocs[num_relocs], drm,
|
||||
file);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
reloc = &job->relocarray[num_relocs];
|
||||
reloc = &job->relocs[num_relocs];
|
||||
obj = host1x_to_tegra_bo(reloc->cmdbuf.bo);
|
||||
refs[num_refs++] = &obj->gem;
|
||||
|
||||
@@ -468,30 +443,6 @@ int tegra_drm_submit(struct tegra_drm_context *context,
|
||||
}
|
||||
}
|
||||
|
||||
/* copy and resolve waitchks from submit */
|
||||
while (num_waitchks--) {
|
||||
struct host1x_waitchk *wait = &job->waitchk[num_waitchks];
|
||||
struct tegra_bo *obj;
|
||||
|
||||
err = host1x_waitchk_copy_from_user(
|
||||
wait, &user_waitchks[num_waitchks], file);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
obj = host1x_to_tegra_bo(wait->bo);
|
||||
refs[num_refs++] = &obj->gem;
|
||||
|
||||
/*
|
||||
* The unaligned offset will cause an unaligned write during
|
||||
* of the waitchks patching, corrupting the commands stream.
|
||||
*/
|
||||
if (wait->offset & 3 ||
|
||||
wait->offset >= obj->gem.size) {
|
||||
err = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (copy_from_user(&syncpt, user_syncpt, sizeof(syncpt))) {
|
||||
err = -EFAULT;
|
||||
goto fail;
|
||||
@@ -1101,6 +1052,52 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct iommu_group *host1x_client_iommu_attach(struct host1x_client *client,
|
||||
bool shared)
|
||||
{
|
||||
struct drm_device *drm = dev_get_drvdata(client->parent);
|
||||
struct tegra_drm *tegra = drm->dev_private;
|
||||
struct iommu_group *group = NULL;
|
||||
int err;
|
||||
|
||||
if (tegra->domain) {
|
||||
group = iommu_group_get(client->dev);
|
||||
if (!group) {
|
||||
dev_err(client->dev, "failed to get IOMMU group\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
if (!shared || (shared && (group != tegra->group))) {
|
||||
err = iommu_attach_group(tegra->domain, group);
|
||||
if (err < 0) {
|
||||
iommu_group_put(group);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
if (shared && !tegra->group)
|
||||
tegra->group = group;
|
||||
}
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
void host1x_client_iommu_detach(struct host1x_client *client,
|
||||
struct iommu_group *group)
|
||||
{
|
||||
struct drm_device *drm = dev_get_drvdata(client->parent);
|
||||
struct tegra_drm *tegra = drm->dev_private;
|
||||
|
||||
if (group) {
|
||||
if (group == tegra->group) {
|
||||
iommu_detach_group(tegra->domain, group);
|
||||
tegra->group = NULL;
|
||||
}
|
||||
|
||||
iommu_group_put(group);
|
||||
}
|
||||
}
|
||||
|
||||
void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, dma_addr_t *dma)
|
||||
{
|
||||
struct iova *alloc;
|
||||
|
@@ -29,16 +29,10 @@
|
||||
|
||||
struct reset_control;
|
||||
|
||||
struct tegra_fb {
|
||||
struct drm_framebuffer base;
|
||||
struct tegra_bo **planes;
|
||||
unsigned int num_planes;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DRM_FBDEV_EMULATION
|
||||
struct tegra_fbdev {
|
||||
struct drm_fb_helper base;
|
||||
struct tegra_fb *fb;
|
||||
struct drm_framebuffer *fb;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -97,6 +91,7 @@ struct tegra_drm_client {
|
||||
struct host1x_client base;
|
||||
struct list_head list;
|
||||
|
||||
unsigned int version;
|
||||
const struct tegra_drm_client_ops *ops;
|
||||
};
|
||||
|
||||
@@ -110,6 +105,10 @@ 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 tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm);
|
||||
int tegra_drm_exit(struct tegra_drm *tegra);
|
||||
|
@@ -14,11 +14,7 @@
|
||||
|
||||
#include "drm.h"
|
||||
#include "gem.h"
|
||||
|
||||
static inline struct tegra_fb *to_tegra_fb(struct drm_framebuffer *fb)
|
||||
{
|
||||
return container_of(fb, struct tegra_fb, base);
|
||||
}
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
|
||||
#ifdef CONFIG_DRM_FBDEV_EMULATION
|
||||
static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper)
|
||||
@@ -30,19 +26,14 @@ static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper)
|
||||
struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
|
||||
unsigned int index)
|
||||
{
|
||||
struct tegra_fb *fb = to_tegra_fb(framebuffer);
|
||||
|
||||
if (index >= framebuffer->format->num_planes)
|
||||
return NULL;
|
||||
|
||||
return fb->planes[index];
|
||||
return to_tegra_bo(drm_gem_fb_get_obj(framebuffer, index));
|
||||
}
|
||||
|
||||
bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer)
|
||||
{
|
||||
struct tegra_fb *fb = to_tegra_fb(framebuffer);
|
||||
struct tegra_bo *bo = tegra_fb_get_plane(framebuffer, 0);
|
||||
|
||||
if (fb->planes[0]->flags & TEGRA_BO_BOTTOM_UP)
|
||||
if (bo->flags & TEGRA_BO_BOTTOM_UP)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -51,8 +42,7 @@ bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer)
|
||||
int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
|
||||
struct tegra_bo_tiling *tiling)
|
||||
{
|
||||
struct tegra_fb *fb = to_tegra_fb(framebuffer);
|
||||
uint64_t modifier = fb->base.modifier;
|
||||
uint64_t modifier = framebuffer->modifier;
|
||||
|
||||
switch (modifier) {
|
||||
case DRM_FORMAT_MOD_LINEAR:
|
||||
@@ -102,46 +92,17 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_fb_destroy(struct drm_framebuffer *framebuffer)
|
||||
{
|
||||
struct tegra_fb *fb = to_tegra_fb(framebuffer);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < fb->num_planes; i++) {
|
||||
struct tegra_bo *bo = fb->planes[i];
|
||||
|
||||
if (bo) {
|
||||
if (bo->pages)
|
||||
vunmap(bo->vaddr);
|
||||
|
||||
drm_gem_object_put_unlocked(&bo->gem);
|
||||
}
|
||||
}
|
||||
|
||||
drm_framebuffer_cleanup(framebuffer);
|
||||
kfree(fb->planes);
|
||||
kfree(fb);
|
||||
}
|
||||
|
||||
static int tegra_fb_create_handle(struct drm_framebuffer *framebuffer,
|
||||
struct drm_file *file, unsigned int *handle)
|
||||
{
|
||||
struct tegra_fb *fb = to_tegra_fb(framebuffer);
|
||||
|
||||
return drm_gem_handle_create(file, &fb->planes[0]->gem, handle);
|
||||
}
|
||||
|
||||
static const struct drm_framebuffer_funcs tegra_fb_funcs = {
|
||||
.destroy = tegra_fb_destroy,
|
||||
.create_handle = tegra_fb_create_handle,
|
||||
.destroy = drm_gem_fb_destroy,
|
||||
.create_handle = drm_gem_fb_create_handle,
|
||||
};
|
||||
|
||||
static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm,
|
||||
const struct drm_mode_fb_cmd2 *mode_cmd,
|
||||
struct tegra_bo **planes,
|
||||
unsigned int num_planes)
|
||||
static struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm,
|
||||
const struct drm_mode_fb_cmd2 *mode_cmd,
|
||||
struct tegra_bo **planes,
|
||||
unsigned int num_planes)
|
||||
{
|
||||
struct tegra_fb *fb;
|
||||
struct drm_framebuffer *fb;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
@@ -149,24 +110,15 @@ static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm,
|
||||
if (!fb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fb->planes = kzalloc(num_planes * sizeof(*planes), GFP_KERNEL);
|
||||
if (!fb->planes) {
|
||||
kfree(fb);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
drm_helper_mode_fill_fb_struct(drm, fb, mode_cmd);
|
||||
|
||||
fb->num_planes = num_planes;
|
||||
for (i = 0; i < fb->format->num_planes; i++)
|
||||
fb->obj[i] = &planes[i]->gem;
|
||||
|
||||
drm_helper_mode_fill_fb_struct(drm, &fb->base, mode_cmd);
|
||||
|
||||
for (i = 0; i < fb->num_planes; i++)
|
||||
fb->planes[i] = planes[i];
|
||||
|
||||
err = drm_framebuffer_init(drm, &fb->base, &tegra_fb_funcs);
|
||||
err = drm_framebuffer_init(drm, fb, &tegra_fb_funcs);
|
||||
if (err < 0) {
|
||||
dev_err(drm->dev, "failed to initialize framebuffer: %d\n",
|
||||
err);
|
||||
kfree(fb->planes);
|
||||
kfree(fb);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
@@ -181,7 +133,7 @@ struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
|
||||
unsigned int hsub, vsub, i;
|
||||
struct tegra_bo *planes[4];
|
||||
struct drm_gem_object *gem;
|
||||
struct tegra_fb *fb;
|
||||
struct drm_framebuffer *fb;
|
||||
int err;
|
||||
|
||||
hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format);
|
||||
@@ -217,7 +169,7 @@ struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
|
||||
goto unreference;
|
||||
}
|
||||
|
||||
return &fb->base;
|
||||
return fb;
|
||||
|
||||
unreference:
|
||||
while (i--)
|
||||
@@ -298,7 +250,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper,
|
||||
return PTR_ERR(fbdev->fb);
|
||||
}
|
||||
|
||||
fb = &fbdev->fb->base;
|
||||
fb = fbdev->fb;
|
||||
helper->fb = fb;
|
||||
helper->fbdev = info;
|
||||
|
||||
@@ -398,8 +350,17 @@ static void tegra_fbdev_exit(struct tegra_fbdev *fbdev)
|
||||
{
|
||||
drm_fb_helper_unregister_fbi(&fbdev->base);
|
||||
|
||||
if (fbdev->fb)
|
||||
drm_framebuffer_remove(&fbdev->fb->base);
|
||||
if (fbdev->fb) {
|
||||
struct tegra_bo *bo = tegra_fb_get_plane(fbdev->fb, 0);
|
||||
|
||||
/* Undo the special mapping we made in fbdev probe. */
|
||||
if (bo && bo->pages) {
|
||||
vunmap(bo->vaddr);
|
||||
bo->vaddr = 0;
|
||||
}
|
||||
|
||||
drm_framebuffer_remove(fbdev->fb);
|
||||
}
|
||||
|
||||
drm_fb_helper_fini(&fbdev->base);
|
||||
tegra_fbdev_free(fbdev);
|
||||
|
@@ -422,14 +422,13 @@ int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_bo_fault(struct vm_fault *vmf)
|
||||
static vm_fault_t tegra_bo_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct drm_gem_object *gem = vma->vm_private_data;
|
||||
struct tegra_bo *bo = to_tegra_bo(gem);
|
||||
struct page *page;
|
||||
pgoff_t offset;
|
||||
int err;
|
||||
|
||||
if (!bo->pages)
|
||||
return VM_FAULT_SIGBUS;
|
||||
@@ -437,20 +436,7 @@ static int tegra_bo_fault(struct vm_fault *vmf)
|
||||
offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
|
||||
page = bo->pages[offset];
|
||||
|
||||
err = vm_insert_page(vma, vmf->address, page);
|
||||
switch (err) {
|
||||
case -EAGAIN:
|
||||
case 0:
|
||||
case -ERESTARTSYS:
|
||||
case -EINTR:
|
||||
case -EBUSY:
|
||||
return VM_FAULT_NOPAGE;
|
||||
|
||||
case -ENOMEM:
|
||||
return VM_FAULT_OOM;
|
||||
}
|
||||
|
||||
return VM_FAULT_SIGBUS;
|
||||
return vmf_insert_page(vma, vmf->address, page);
|
||||
}
|
||||
|
||||
const struct vm_operations_struct tegra_bo_vm_ops = {
|
||||
@@ -649,6 +635,8 @@ struct dma_buf *tegra_gem_prime_export(struct drm_device *drm,
|
||||
{
|
||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
|
||||
|
||||
exp_info.exp_name = KBUILD_MODNAME;
|
||||
exp_info.owner = drm->driver->fops->owner;
|
||||
exp_info.ops = &tegra_gem_prime_dmabuf_ops;
|
||||
exp_info.size = gem->size;
|
||||
exp_info.flags = flags;
|
||||
|
@@ -7,16 +7,25 @@
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "drm.h"
|
||||
#include "gem.h"
|
||||
#include "gr2d.h"
|
||||
|
||||
struct gr2d_soc {
|
||||
unsigned int version;
|
||||
};
|
||||
|
||||
struct gr2d {
|
||||
struct iommu_group *group;
|
||||
struct tegra_drm_client client;
|
||||
struct host1x_channel *channel;
|
||||
struct clk *clk;
|
||||
|
||||
const struct gr2d_soc *soc;
|
||||
|
||||
DECLARE_BITMAP(addr_regs, GR2D_NUM_REGS);
|
||||
};
|
||||
|
||||
@@ -31,6 +40,7 @@ static int gr2d_init(struct host1x_client *client)
|
||||
struct drm_device *dev = dev_get_drvdata(client->parent);
|
||||
unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
|
||||
struct gr2d *gr2d = to_gr2d(drm);
|
||||
int err;
|
||||
|
||||
gr2d->channel = host1x_channel_request(client->dev);
|
||||
if (!gr2d->channel)
|
||||
@@ -38,24 +48,48 @@ static int gr2d_init(struct host1x_client *client)
|
||||
|
||||
client->syncpts[0] = host1x_syncpt_request(client, flags);
|
||||
if (!client->syncpts[0]) {
|
||||
host1x_channel_put(gr2d->channel);
|
||||
return -ENOMEM;
|
||||
err = -ENOMEM;
|
||||
dev_err(client->dev, "failed to request syncpoint: %d\n", err);
|
||||
goto put;
|
||||
}
|
||||
|
||||
return tegra_drm_register_client(dev->dev_private, drm);
|
||||
gr2d->group = host1x_client_iommu_attach(client, false);
|
||||
if (IS_ERR(gr2d->group)) {
|
||||
err = PTR_ERR(gr2d->group);
|
||||
dev_err(client->dev, "failed to attach to domain: %d\n", err);
|
||||
goto free;
|
||||
}
|
||||
|
||||
err = tegra_drm_register_client(dev->dev_private, drm);
|
||||
if (err < 0) {
|
||||
dev_err(client->dev, "failed to register client: %d\n", err);
|
||||
goto detach;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
detach:
|
||||
host1x_client_iommu_detach(client, gr2d->group);
|
||||
free:
|
||||
host1x_syncpt_free(client->syncpts[0]);
|
||||
put:
|
||||
host1x_channel_put(gr2d->channel);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gr2d_exit(struct host1x_client *client)
|
||||
{
|
||||
struct tegra_drm_client *drm = host1x_to_drm_client(client);
|
||||
struct drm_device *dev = dev_get_drvdata(client->parent);
|
||||
struct tegra_drm *tegra = dev->dev_private;
|
||||
struct gr2d *gr2d = to_gr2d(drm);
|
||||
int err;
|
||||
|
||||
err = tegra_drm_unregister_client(dev->dev_private, drm);
|
||||
err = tegra_drm_unregister_client(tegra, drm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
host1x_client_iommu_detach(client, gr2d->group);
|
||||
host1x_syncpt_free(client->syncpts[0]);
|
||||
host1x_channel_put(gr2d->channel);
|
||||
|
||||
@@ -123,9 +157,17 @@ static const struct tegra_drm_client_ops gr2d_ops = {
|
||||
.submit = tegra_drm_submit,
|
||||
};
|
||||
|
||||
static const struct gr2d_soc tegra20_gr2d_soc = {
|
||||
.version = 0x20,
|
||||
};
|
||||
|
||||
static const struct gr2d_soc tegra30_gr2d_soc = {
|
||||
.version = 0x30,
|
||||
};
|
||||
|
||||
static const struct of_device_id gr2d_match[] = {
|
||||
{ .compatible = "nvidia,tegra30-gr2d" },
|
||||
{ .compatible = "nvidia,tegra20-gr2d" },
|
||||
{ .compatible = "nvidia,tegra30-gr2d", .data = &tegra20_gr2d_soc },
|
||||
{ .compatible = "nvidia,tegra20-gr2d", .data = &tegra30_gr2d_soc },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gr2d_match);
|
||||
@@ -158,6 +200,8 @@ static int gr2d_probe(struct platform_device *pdev)
|
||||
if (!gr2d)
|
||||
return -ENOMEM;
|
||||
|
||||
gr2d->soc = of_device_get_match_data(dev);
|
||||
|
||||
syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
|
||||
if (!syncpts)
|
||||
return -ENOMEM;
|
||||
@@ -182,6 +226,7 @@ static int gr2d_probe(struct platform_device *pdev)
|
||||
gr2d->client.base.num_syncpts = 1;
|
||||
|
||||
INIT_LIST_HEAD(&gr2d->client.list);
|
||||
gr2d->client.version = gr2d->soc->version;
|
||||
gr2d->client.ops = &gr2d_ops;
|
||||
|
||||
err = host1x_client_register(&gr2d->client.base);
|
||||
|
@@ -9,7 +9,9 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/host1x.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
@@ -19,7 +21,12 @@
|
||||
#include "gem.h"
|
||||
#include "gr3d.h"
|
||||
|
||||
struct gr3d_soc {
|
||||
unsigned int version;
|
||||
};
|
||||
|
||||
struct gr3d {
|
||||
struct iommu_group *group;
|
||||
struct tegra_drm_client client;
|
||||
struct host1x_channel *channel;
|
||||
struct clk *clk_secondary;
|
||||
@@ -27,6 +34,8 @@ struct gr3d {
|
||||
struct reset_control *rst_secondary;
|
||||
struct reset_control *rst;
|
||||
|
||||
const struct gr3d_soc *soc;
|
||||
|
||||
DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS);
|
||||
};
|
||||
|
||||
@@ -41,6 +50,7 @@ static int gr3d_init(struct host1x_client *client)
|
||||
struct drm_device *dev = dev_get_drvdata(client->parent);
|
||||
unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
|
||||
struct gr3d *gr3d = to_gr3d(drm);
|
||||
int err;
|
||||
|
||||
gr3d->channel = host1x_channel_request(client->dev);
|
||||
if (!gr3d->channel)
|
||||
@@ -48,11 +58,33 @@ static int gr3d_init(struct host1x_client *client)
|
||||
|
||||
client->syncpts[0] = host1x_syncpt_request(client, flags);
|
||||
if (!client->syncpts[0]) {
|
||||
host1x_channel_put(gr3d->channel);
|
||||
return -ENOMEM;
|
||||
err = -ENOMEM;
|
||||
dev_err(client->dev, "failed to request syncpoint: %d\n", err);
|
||||
goto put;
|
||||
}
|
||||
|
||||
return tegra_drm_register_client(dev->dev_private, drm);
|
||||
gr3d->group = host1x_client_iommu_attach(client, false);
|
||||
if (IS_ERR(gr3d->group)) {
|
||||
err = PTR_ERR(gr3d->group);
|
||||
dev_err(client->dev, "failed to attach to domain: %d\n", err);
|
||||
goto free;
|
||||
}
|
||||
|
||||
err = tegra_drm_register_client(dev->dev_private, drm);
|
||||
if (err < 0) {
|
||||
dev_err(client->dev, "failed to register client: %d\n", err);
|
||||
goto detach;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
detach:
|
||||
host1x_client_iommu_detach(client, gr3d->group);
|
||||
free:
|
||||
host1x_syncpt_free(client->syncpts[0]);
|
||||
put:
|
||||
host1x_channel_put(gr3d->channel);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gr3d_exit(struct host1x_client *client)
|
||||
@@ -66,6 +98,7 @@ static int gr3d_exit(struct host1x_client *client)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
host1x_client_iommu_detach(client, gr3d->group);
|
||||
host1x_syncpt_free(client->syncpts[0]);
|
||||
host1x_channel_put(gr3d->channel);
|
||||
|
||||
@@ -125,10 +158,22 @@ static const struct tegra_drm_client_ops gr3d_ops = {
|
||||
.submit = tegra_drm_submit,
|
||||
};
|
||||
|
||||
static const struct gr3d_soc tegra20_gr3d_soc = {
|
||||
.version = 0x20,
|
||||
};
|
||||
|
||||
static const struct gr3d_soc tegra30_gr3d_soc = {
|
||||
.version = 0x30,
|
||||
};
|
||||
|
||||
static const struct gr3d_soc tegra114_gr3d_soc = {
|
||||
.version = 0x35,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_gr3d_match[] = {
|
||||
{ .compatible = "nvidia,tegra114-gr3d" },
|
||||
{ .compatible = "nvidia,tegra30-gr3d" },
|
||||
{ .compatible = "nvidia,tegra20-gr3d" },
|
||||
{ .compatible = "nvidia,tegra114-gr3d", .data = &tegra114_gr3d_soc },
|
||||
{ .compatible = "nvidia,tegra30-gr3d", .data = &tegra30_gr3d_soc },
|
||||
{ .compatible = "nvidia,tegra20-gr3d", .data = &tegra20_gr3d_soc },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_gr3d_match);
|
||||
@@ -250,6 +295,8 @@ static int gr3d_probe(struct platform_device *pdev)
|
||||
if (!gr3d)
|
||||
return -ENOMEM;
|
||||
|
||||
gr3d->soc = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
syncpts = devm_kzalloc(&pdev->dev, sizeof(*syncpts), GFP_KERNEL);
|
||||
if (!syncpts)
|
||||
return -ENOMEM;
|
||||
@@ -307,6 +354,7 @@ static int gr3d_probe(struct platform_device *pdev)
|
||||
gr3d->client.base.num_syncpts = 1;
|
||||
|
||||
INIT_LIST_HEAD(&gr3d->client.list);
|
||||
gr3d->client.version = gr3d->soc->version;
|
||||
gr3d->client.ops = &gr3d_ops;
|
||||
|
||||
err = host1x_client_register(&gr3d->client.base);
|
||||
|
@@ -687,7 +687,7 @@ void tegra_display_hub_atomic_commit(struct drm_device *drm,
|
||||
struct device *dev = hub->client.dev;
|
||||
int err;
|
||||
|
||||
hub_state = tegra_display_hub_get_state(hub, state);
|
||||
hub_state = to_tegra_display_hub_state(hub->base.state);
|
||||
|
||||
if (hub_state->clk) {
|
||||
err = clk_set_rate(hub_state->clk, hub_state->rate);
|
||||
|
@@ -23,6 +23,7 @@ static void tegra_plane_destroy(struct drm_plane *plane)
|
||||
|
||||
static void tegra_plane_reset(struct drm_plane *plane)
|
||||
{
|
||||
struct tegra_plane *p = to_tegra_plane(plane);
|
||||
struct tegra_plane_state *state;
|
||||
|
||||
if (plane->state)
|
||||
@@ -35,6 +36,8 @@ static void tegra_plane_reset(struct drm_plane *plane)
|
||||
if (state) {
|
||||
plane->state = &state->base;
|
||||
plane->state->plane = plane;
|
||||
plane->state->zpos = p->index;
|
||||
plane->state->normalized_zpos = p->index;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,10 +56,11 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
|
||||
copy->tiling = state->tiling;
|
||||
copy->format = state->format;
|
||||
copy->swap = state->swap;
|
||||
copy->bottom_up = state->bottom_up;
|
||||
copy->opaque = state->opaque;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
copy->dependent[i] = state->dependent[i];
|
||||
for (i = 0; i < 2; i++)
|
||||
copy->blending[i] = state->blending[i];
|
||||
|
||||
return ©->base;
|
||||
}
|
||||
@@ -267,24 +271,8 @@ static bool __drm_format_has_alpha(u32 format)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is applicable to Tegra20 and Tegra30 only where the opaque formats can
|
||||
* be emulated using the alpha formats and alpha blending disabled.
|
||||
*/
|
||||
bool tegra_plane_format_has_alpha(unsigned int format)
|
||||
{
|
||||
switch (format) {
|
||||
case WIN_COLOR_DEPTH_B5G5R5A1:
|
||||
case WIN_COLOR_DEPTH_A1B5G5R5:
|
||||
case WIN_COLOR_DEPTH_R8G8B8A8:
|
||||
case WIN_COLOR_DEPTH_B8G8R8A8:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha)
|
||||
static int tegra_plane_format_get_alpha(unsigned int opaque,
|
||||
unsigned int *alpha)
|
||||
{
|
||||
if (tegra_plane_format_is_yuv(opaque, NULL)) {
|
||||
*alpha = opaque;
|
||||
@@ -316,6 +304,67 @@ int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is applicable to Tegra20 and Tegra30 only where the opaque formats can
|
||||
* be emulated using the alpha formats and alpha blending disabled.
|
||||
*/
|
||||
static int tegra_plane_setup_opacity(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state)
|
||||
{
|
||||
unsigned int format;
|
||||
int err;
|
||||
|
||||
switch (state->format) {
|
||||
case WIN_COLOR_DEPTH_B5G5R5A1:
|
||||
case WIN_COLOR_DEPTH_A1B5G5R5:
|
||||
case WIN_COLOR_DEPTH_R8G8B8A8:
|
||||
case WIN_COLOR_DEPTH_B8G8R8A8:
|
||||
state->opaque = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = tegra_plane_format_get_alpha(state->format, &format);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
state->format = format;
|
||||
state->opaque = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_plane_check_transparency(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state)
|
||||
{
|
||||
struct drm_plane_state *old, *plane_state;
|
||||
struct drm_plane *plane;
|
||||
|
||||
old = drm_atomic_get_old_plane_state(state->base.state, &tegra->base);
|
||||
|
||||
/* check if zpos / transparency changed */
|
||||
if (old->normalized_zpos == state->base.normalized_zpos &&
|
||||
to_tegra_plane_state(old)->opaque == state->opaque)
|
||||
return 0;
|
||||
|
||||
/* include all sibling planes into this commit */
|
||||
drm_for_each_plane(plane, tegra->base.dev) {
|
||||
struct tegra_plane *p = to_tegra_plane(plane);
|
||||
|
||||
/* skip this plane and planes on different CRTCs */
|
||||
if (p == tegra || p->dc != tegra->dc)
|
||||
continue;
|
||||
|
||||
plane_state = drm_atomic_get_plane_state(state->base.state,
|
||||
plane);
|
||||
if (IS_ERR(plane_state))
|
||||
return PTR_ERR(plane_state);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
|
||||
struct tegra_plane *other)
|
||||
{
|
||||
@@ -336,61 +385,98 @@ static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
|
||||
return index;
|
||||
}
|
||||
|
||||
void tegra_plane_check_dependent(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state)
|
||||
static void tegra_plane_update_transparency(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state)
|
||||
{
|
||||
struct drm_plane_state *old, *new;
|
||||
struct drm_plane_state *new;
|
||||
struct drm_plane *plane;
|
||||
unsigned int zpos[2];
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
zpos[i] = 0;
|
||||
|
||||
for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) {
|
||||
for_each_new_plane_in_state(state->base.state, plane, new, i) {
|
||||
struct tegra_plane *p = to_tegra_plane(plane);
|
||||
unsigned index;
|
||||
|
||||
/* skip this plane and planes on different CRTCs */
|
||||
if (p == tegra || new->crtc != state->base.crtc)
|
||||
if (p == tegra || p->dc != tegra->dc)
|
||||
continue;
|
||||
|
||||
index = tegra_plane_get_overlap_index(tegra, p);
|
||||
|
||||
state->dependent[index] = false;
|
||||
if (new->fb && __drm_format_has_alpha(new->fb->format->format))
|
||||
state->blending[index].alpha = true;
|
||||
else
|
||||
state->blending[index].alpha = false;
|
||||
|
||||
if (new->normalized_zpos > state->base.normalized_zpos)
|
||||
state->blending[index].top = true;
|
||||
else
|
||||
state->blending[index].top = false;
|
||||
|
||||
/*
|
||||
* If any of the other planes is on top of this plane and uses
|
||||
* a format with an alpha component, mark this plane as being
|
||||
* dependent, meaning it's alpha value will be 1 minus the sum
|
||||
* of alpha components of the overlapping planes.
|
||||
* Missing framebuffer means that plane is disabled, in this
|
||||
* case mark B / C window as top to be able to differentiate
|
||||
* windows indices order in regards to zPos for the middle
|
||||
* window X / Y registers programming.
|
||||
*/
|
||||
if (p->index > tegra->index) {
|
||||
if (__drm_format_has_alpha(new->fb->format->format))
|
||||
state->dependent[index] = true;
|
||||
|
||||
/* keep track of the Z position */
|
||||
zpos[index] = p->index;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The region where three windows overlap is the intersection of the
|
||||
* two regions where two windows overlap. It contributes to the area
|
||||
* if any of the windows on top of it have an alpha component.
|
||||
*/
|
||||
for (i = 0; i < 2; i++)
|
||||
state->dependent[2] = state->dependent[2] ||
|
||||
state->dependent[i];
|
||||
|
||||
/*
|
||||
* However, if any of the windows on top of this window is opaque, it
|
||||
* will completely conceal this window within that area, so avoid the
|
||||
* window from contributing to the area.
|
||||
*/
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (zpos[i] > tegra->index)
|
||||
state->dependent[2] = state->dependent[2] &&
|
||||
state->dependent[i];
|
||||
if (!new->fb)
|
||||
state->blending[index].top = (index == 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int tegra_plane_setup_transparency(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state)
|
||||
{
|
||||
struct tegra_plane_state *tegra_state;
|
||||
struct drm_plane_state *new;
|
||||
struct drm_plane *plane;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* If planes zpos / transparency changed, sibling planes blending
|
||||
* state may require adjustment and in this case they will be included
|
||||
* into this atom commit, otherwise blending state is unchanged.
|
||||
*/
|
||||
err = tegra_plane_check_transparency(tegra, state);
|
||||
if (err <= 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* All planes are now in the atomic state, walk them up and update
|
||||
* transparency state for each plane.
|
||||
*/
|
||||
drm_for_each_plane(plane, tegra->base.dev) {
|
||||
struct tegra_plane *p = to_tegra_plane(plane);
|
||||
|
||||
/* skip planes on different CRTCs */
|
||||
if (p->dc != tegra->dc)
|
||||
continue;
|
||||
|
||||
new = drm_atomic_get_new_plane_state(state->base.state, plane);
|
||||
tegra_state = to_tegra_plane_state(new);
|
||||
|
||||
/*
|
||||
* There is no need to update blending state for the disabled
|
||||
* plane.
|
||||
*/
|
||||
if (new->fb)
|
||||
tegra_plane_update_transparency(p, tegra_state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = tegra_plane_setup_opacity(tegra, state);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = tegra_plane_setup_transparency(tegra, state);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -34,6 +34,11 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
|
||||
return container_of(plane, struct tegra_plane, base);
|
||||
}
|
||||
|
||||
struct tegra_plane_legacy_blending_state {
|
||||
bool alpha;
|
||||
bool top;
|
||||
};
|
||||
|
||||
struct tegra_plane_state {
|
||||
struct drm_plane_state base;
|
||||
|
||||
@@ -41,9 +46,11 @@ struct tegra_plane_state {
|
||||
u32 format;
|
||||
u32 swap;
|
||||
|
||||
bool bottom_up;
|
||||
|
||||
/* used for legacy blending support only */
|
||||
struct tegra_plane_legacy_blending_state blending[2];
|
||||
bool opaque;
|
||||
bool dependent[3];
|
||||
};
|
||||
|
||||
static inline struct tegra_plane_state *
|
||||
@@ -62,9 +69,7 @@ int tegra_plane_state_add(struct tegra_plane *plane,
|
||||
|
||||
int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap);
|
||||
bool tegra_plane_format_is_yuv(unsigned int format, bool *planar);
|
||||
bool tegra_plane_format_has_alpha(unsigned int format);
|
||||
int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha);
|
||||
void tegra_plane_check_dependent(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state);
|
||||
int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
|
||||
struct tegra_plane_state *state);
|
||||
|
||||
#endif /* TEGRA_PLANE_H */
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
struct vic_config {
|
||||
const char *firmware;
|
||||
unsigned int version;
|
||||
};
|
||||
|
||||
struct vic {
|
||||
@@ -264,18 +265,21 @@ 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,
|
||||
};
|
||||
|
||||
#define NVIDIA_TEGRA_210_VIC_FIRMWARE "nvidia/tegra210/vic04_ucode.bin"
|
||||
|
||||
static const struct vic_config vic_t210_config = {
|
||||
.firmware = NVIDIA_TEGRA_210_VIC_FIRMWARE,
|
||||
.version = 0x21,
|
||||
};
|
||||
|
||||
#define NVIDIA_TEGRA_186_VIC_FIRMWARE "nvidia/tegra186/vic04_ucode.bin"
|
||||
|
||||
static const struct vic_config vic_t186_config = {
|
||||
.firmware = NVIDIA_TEGRA_186_VIC_FIRMWARE,
|
||||
.version = 0x18,
|
||||
};
|
||||
|
||||
static const struct of_device_id vic_match[] = {
|
||||
@@ -342,6 +346,7 @@ static int vic_probe(struct platform_device *pdev)
|
||||
vic->dev = dev;
|
||||
|
||||
INIT_LIST_HEAD(&vic->client.list);
|
||||
vic->client.version = vic->config->version;
|
||||
vic->client.ops = &vic_ops;
|
||||
|
||||
err = host1x_client_register(&vic->client.base);
|
||||
|
Reference in New Issue
Block a user