From e1e906448d2fc6f5a69e1967e00868f0cbfbb566 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 24 Sep 2013 13:59:01 +0200 Subject: gpu: host1x: Make host1x header file public In preparation to support host1x clients other than DRM, move this header into a public location. Signed-off-by: Thierry Reding --- include/linux/host1x.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 include/linux/host1x.h (limited to 'include') diff --git a/include/linux/host1x.h b/include/linux/host1x.h new file mode 100644 index 000000000000..fe09939800bc --- /dev/null +++ b/include/linux/host1x.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2009-2013, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __LINUX_HOST1X_H +#define __LINUX_HOST1X_H + +enum host1x_class { + HOST1X_CLASS_HOST1X = 0x1, + HOST1X_CLASS_GR2D = 0x51, + HOST1X_CLASS_GR2D_SB = 0x52, +}; + +#endif -- cgit v1.2.3 From 53fa7f7204c97dc0c86b99ff8365ad6a7b2ebd78 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 24 Sep 2013 15:35:40 +0200 Subject: drm/tegra: Introduce tegra_drm_client structure This structure derives from host1x_client. DRM-specific fields are moved from host1x_client to this structure, so that host1x_client can remain agnostic of DRM. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/drm/dc.c | 35 ++++++++++++++++++----------------- drivers/gpu/host1x/drm/drm.c | 30 +++++++++++++++++++----------- drivers/gpu/host1x/drm/drm.h | 37 +++++++++++++++++-------------------- drivers/gpu/host1x/drm/gr2d.c | 42 +++++++++++++++++++++++------------------- drivers/gpu/host1x/drm/hdmi.c | 33 +++++++++++++++++---------------- include/linux/host1x.h | 20 ++++++++++++++++++++ 6 files changed, 114 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c index e11aec779a15..5106df08f046 100644 --- a/drivers/gpu/host1x/drm/dc.c +++ b/drivers/gpu/host1x/drm/dc.c @@ -1038,30 +1038,30 @@ static int tegra_dc_debugfs_exit(struct tegra_dc *dc) return 0; } -static int tegra_dc_drm_init(struct host1x_client *client, - struct drm_device *drm) +static int tegra_dc_init(struct host1x_client *client) { - struct tegra_dc *dc = host1x_client_to_dc(client); + struct tegra_drm_client *drm = to_tegra_drm_client(client); + struct tegra_dc *dc = tegra_drm_client_to_dc(drm); int err; - dc->pipe = drm->mode_config.num_crtc; + dc->pipe = drm->drm->mode_config.num_crtc; - drm_crtc_init(drm, &dc->base, &tegra_crtc_funcs); + drm_crtc_init(drm->drm, &dc->base, &tegra_crtc_funcs); drm_mode_crtc_set_gamma_size(&dc->base, 256); drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); - err = tegra_dc_rgb_init(drm, dc); + err = tegra_dc_rgb_init(drm->drm, dc); if (err < 0 && err != -ENODEV) { dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); return err; } - err = tegra_dc_add_planes(drm, dc); + err = tegra_dc_add_planes(drm->drm, dc); if (err < 0) return err; if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dc_debugfs_init(dc, drm->primary); + err = tegra_dc_debugfs_init(dc, drm->drm->primary); if (err < 0) dev_err(dc->dev, "debugfs setup failed: %d\n", err); } @@ -1077,9 +1077,10 @@ static int tegra_dc_drm_init(struct host1x_client *client, return 0; } -static int tegra_dc_drm_exit(struct host1x_client *client) +static int tegra_dc_exit(struct host1x_client *client) { - struct tegra_dc *dc = host1x_client_to_dc(client); + struct tegra_drm_client *drm = to_tegra_drm_client(client); + struct tegra_dc *dc = tegra_drm_client_to_dc(drm); int err; devm_free_irq(dc->dev, dc->irq, dc); @@ -1100,8 +1101,8 @@ static int tegra_dc_drm_exit(struct host1x_client *client) } static const struct host1x_client_ops dc_client_ops = { - .drm_init = tegra_dc_drm_init, - .drm_exit = tegra_dc_drm_exit, + .init = tegra_dc_init, + .exit = tegra_dc_exit, }; static int tegra_dc_probe(struct platform_device *pdev) @@ -1140,9 +1141,9 @@ static int tegra_dc_probe(struct platform_device *pdev) return -ENXIO; } - INIT_LIST_HEAD(&dc->client.list); - dc->client.ops = &dc_client_ops; - dc->client.dev = &pdev->dev; + INIT_LIST_HEAD(&dc->client.base.list); + dc->client.base.ops = &dc_client_ops; + dc->client.base.dev = &pdev->dev; err = tegra_dc_rgb_probe(dc); if (err < 0 && err != -ENODEV) { @@ -1150,7 +1151,7 @@ static int tegra_dc_probe(struct platform_device *pdev) return err; } - err = host1x_register_client(tegra, &dc->client); + err = host1x_register_client(tegra, &dc->client.base); if (err < 0) { dev_err(&pdev->dev, "failed to register host1x client: %d\n", err); @@ -1168,7 +1169,7 @@ static int tegra_dc_remove(struct platform_device *pdev) struct tegra_dc *dc = platform_get_drvdata(pdev); int err; - err = host1x_unregister_client(tegra, &dc->client); + err = host1x_unregister_client(tegra, &dc->client.base); if (err < 0) { dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", err); diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index 4e503613536b..33d966145293 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c @@ -131,12 +131,18 @@ int tegra_drm_alloc(struct platform_device *pdev) int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm) { struct host1x_client *client; + int err; mutex_lock(&tegra->clients_lock); list_for_each_entry(client, &tegra->clients, list) { - if (client->ops && client->ops->drm_init) { - int err = client->ops->drm_init(client, drm); + struct tegra_drm_client *tdc = to_tegra_drm_client(client); + + /* associate client with DRM device */ + tdc->drm = drm; + + if (client->ops && client->ops->init) { + err = client->ops->init(client); if (err < 0) { dev_err(tegra->dev, "DRM setup failed for %s: %d\n", @@ -154,8 +160,9 @@ int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm) int tegra_drm_exit(struct tegra_drm *tegra) { - struct platform_device *pdev = to_platform_device(tegra->dev); struct host1x_client *client; + struct platform_device *pdev; + int err; if (!tegra->drm) return 0; @@ -163,8 +170,8 @@ int tegra_drm_exit(struct tegra_drm *tegra) mutex_lock(&tegra->clients_lock); list_for_each_entry_reverse(client, &tegra->clients, list) { - if (client->ops && client->ops->drm_exit) { - int err = client->ops->drm_exit(client); + if (client->ops && client->ops->exit) { + err = client->ops->exit(client); if (err < 0) { dev_err(tegra->dev, "DRM cleanup failed for %s: %d\n", @@ -177,6 +184,7 @@ int tegra_drm_exit(struct tegra_drm *tegra) mutex_unlock(&tegra->clients_lock); + pdev = to_platform_device(tegra->dev); drm_platform_exit(&tegra_drm_driver, pdev); tegra->drm = NULL; @@ -409,22 +417,22 @@ static int tegra_open_channel(struct drm_device *drm, void *data, struct tegra_drm *tegra = drm->dev_private; struct drm_tegra_open_channel *args = data; struct tegra_drm_context *context; - struct host1x_client *client; + struct tegra_drm_client *client; int err = -ENODEV; context = kzalloc(sizeof(*context), GFP_KERNEL); if (!context) return -ENOMEM; - list_for_each_entry(client, &tegra->clients, list) - if (client->class == args->client) { + list_for_each_entry(client, &tegra->clients, base.list) + if (client->base.class == args->client) { err = client->ops->open_channel(client, context); if (err) break; - context->client = client; list_add(&context->list, &fpriv->contexts); args->context = (uintptr_t)context; + context->client = client; return 0; } @@ -463,10 +471,10 @@ static int tegra_get_syncpt(struct drm_device *drm, void *data, if (!tegra_drm_file_owns_context(fpriv, context)) return -ENODEV; - if (args->index >= context->client->num_syncpts) + if (args->index >= context->client->base.num_syncpts) return -EINVAL; - syncpt = context->client->syncpts[args->index]; + syncpt = context->client->base.syncpts[args->index]; args->id = host1x_syncpt_id(syncpt); return 0; diff --git a/drivers/gpu/host1x/drm/drm.h b/drivers/gpu/host1x/drm/drm.h index 78754f6a9153..8c26c6b1f5e1 100644 --- a/drivers/gpu/host1x/drm/drm.h +++ b/drivers/gpu/host1x/drm/drm.h @@ -44,18 +44,16 @@ struct tegra_drm { struct tegra_fbdev *fbdev; }; -struct host1x_client; +struct tegra_drm_client; struct tegra_drm_context { - struct host1x_client *client; + struct tegra_drm_client *client; struct host1x_channel *channel; struct list_head list; }; -struct host1x_client_ops { - int (*drm_init)(struct host1x_client *client, struct drm_device *drm); - int (*drm_exit)(struct host1x_client *client); - int (*open_channel)(struct host1x_client *client, +struct tegra_drm_client_ops { + int (*open_channel)(struct tegra_drm_client *client, struct tegra_drm_context *context); void (*close_channel)(struct tegra_drm_context *context); int (*submit)(struct tegra_drm_context *context, @@ -63,21 +61,19 @@ struct host1x_client_ops { struct drm_file *file); }; -struct host1x_client { - struct tegra_drm *tegra; - struct device *dev; - - const struct host1x_client_ops *ops; - - enum host1x_class class; - struct host1x_channel *channel; - - struct host1x_syncpt **syncpts; - unsigned int num_syncpts; +struct tegra_drm_client { + struct host1x_client base; + struct drm_device *drm; - struct list_head list; + const struct tegra_drm_client_ops *ops; }; +static inline struct tegra_drm_client * +to_tegra_drm_client(struct host1x_client *client) +{ + return container_of(client, struct tegra_drm_client, base); +} + extern int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm); extern int tegra_drm_exit(struct tegra_drm *tegra); @@ -89,7 +85,7 @@ extern int host1x_unregister_client(struct tegra_drm *tegra, struct tegra_output; struct tegra_dc { - struct host1x_client client; + struct tegra_drm_client client; struct device *dev; spinlock_t lock; @@ -112,7 +108,8 @@ struct tegra_dc { struct drm_pending_vblank_event *event; }; -static inline struct tegra_dc *host1x_client_to_dc(struct host1x_client *client) +static inline struct tegra_dc * +tegra_drm_client_to_dc(struct tegra_drm_client *client) { return container_of(client, struct tegra_dc, client); } diff --git a/drivers/gpu/host1x/drm/gr2d.c b/drivers/gpu/host1x/drm/gr2d.c index 2691e333e0e2..a7edc794c5ce 100644 --- a/drivers/gpu/host1x/drm/gr2d.c +++ b/drivers/gpu/host1x/drm/gr2d.c @@ -27,20 +27,19 @@ #define GR2D_NUM_REGS 0x4d struct gr2d { - struct host1x_client client; + struct tegra_drm_client client; struct host1x_channel *channel; struct clk *clk; DECLARE_BITMAP(addr_regs, GR2D_NUM_REGS); }; -static inline struct gr2d *to_gr2d(struct host1x_client *client) +static inline struct gr2d *to_gr2d(struct tegra_drm_client *client) { return container_of(client, struct gr2d, client); } -static int gr2d_client_init(struct host1x_client *client, - struct drm_device *drm) +static int gr2d_client_init(struct host1x_client *client) { return 0; } @@ -50,7 +49,12 @@ static int gr2d_client_exit(struct host1x_client *client) return 0; } -static int gr2d_open_channel(struct host1x_client *client, +static const struct host1x_client_ops gr2d_client_ops = { + .init = gr2d_client_init, + .exit = gr2d_client_exit, +}; + +static int gr2d_open_channel(struct tegra_drm_client *client, struct tegra_drm_context *context) { struct gr2d *gr2d = to_gr2d(client); @@ -140,7 +144,7 @@ static int gr2d_submit(struct tegra_drm_context *context, job->num_relocs = args->num_relocs; job->num_waitchk = args->num_waitchks; job->client = (u32)args->context; - job->class = context->client->class; + job->class = context->client->base.class; job->serialize = true; while (num_cmdbufs) { @@ -201,7 +205,7 @@ static int gr2d_submit(struct tegra_drm_context *context, if (args->timeout && args->timeout < 10000) job->timeout = args->timeout; - err = host1x_job_pin(job, context->client->dev); + err = host1x_job_pin(job, context->client->base.dev); if (err) goto fail; @@ -221,9 +225,7 @@ fail: return err; } -static struct host1x_client_ops gr2d_client_ops = { - .drm_init = gr2d_client_init, - .drm_exit = gr2d_client_exit, +static const struct tegra_drm_client_ops gr2d_ops = { .open_channel = gr2d_open_channel, .close_channel = gr2d_close_channel, .submit = gr2d_submit, @@ -279,13 +281,15 @@ static int gr2d_probe(struct platform_device *pdev) return -ENOMEM; } - gr2d->client.ops = &gr2d_client_ops; - gr2d->client.dev = dev; - gr2d->client.class = HOST1X_CLASS_GR2D; - gr2d->client.syncpts = syncpts; - gr2d->client.num_syncpts = 1; + INIT_LIST_HEAD(&gr2d->client.base.list); + gr2d->client.base.ops = &gr2d_client_ops; + gr2d->client.base.dev = dev; + gr2d->client.base.class = HOST1X_CLASS_GR2D; + gr2d->client.base.syncpts = syncpts; + gr2d->client.base.num_syncpts = 1; + gr2d->client.ops = &gr2d_ops; - err = host1x_register_client(tegra, &gr2d->client); + err = host1x_register_client(tegra, &gr2d->client.base); if (err < 0) { dev_err(dev, "failed to register host1x client: %d\n", err); return err; @@ -307,15 +311,15 @@ static int gr2d_remove(struct platform_device *pdev) unsigned int i; int err; - err = host1x_unregister_client(tegra, &gr2d->client); + err = host1x_unregister_client(tegra, &gr2d->client.base); if (err < 0) { dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", err); return err; } - for (i = 0; i < gr2d->client.num_syncpts; i++) - host1x_syncpt_free(gr2d->client.syncpts[i]); + for (i = 0; i < gr2d->client.base.num_syncpts; i++) + host1x_syncpt_free(gr2d->client.base.syncpts[i]); host1x_channel_free(gr2d->channel); clk_disable_unprepare(gr2d->clk); diff --git a/drivers/gpu/host1x/drm/hdmi.c b/drivers/gpu/host1x/drm/hdmi.c index 5d8c41cf4f58..30ac9e872b0d 100644 --- a/drivers/gpu/host1x/drm/hdmi.c +++ b/drivers/gpu/host1x/drm/hdmi.c @@ -19,7 +19,7 @@ #include "host1x_client.h" struct tegra_hdmi { - struct host1x_client client; + struct tegra_drm_client client; struct tegra_output output; struct device *dev; @@ -43,7 +43,7 @@ struct tegra_hdmi { }; static inline struct tegra_hdmi * -host1x_client_to_hdmi(struct host1x_client *client) +tegra_drm_client_to_hdmi(struct tegra_drm_client *client) { return container_of(client, struct tegra_hdmi, client); } @@ -1116,24 +1116,24 @@ static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi) return 0; } -static int tegra_hdmi_drm_init(struct host1x_client *client, - struct drm_device *drm) +static int tegra_hdmi_init(struct host1x_client *client) { - struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); + struct tegra_drm_client *drm = to_tegra_drm_client(client); + struct tegra_hdmi *hdmi = tegra_drm_client_to_hdmi(drm); int err; hdmi->output.type = TEGRA_OUTPUT_HDMI; hdmi->output.dev = client->dev; hdmi->output.ops = &hdmi_ops; - err = tegra_output_init(drm, &hdmi->output); + err = tegra_output_init(drm->drm, &hdmi->output); if (err < 0) { dev_err(client->dev, "output setup failed: %d\n", err); return err; } if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_hdmi_debugfs_init(hdmi, drm->primary); + err = tegra_hdmi_debugfs_init(hdmi, drm->drm->primary); if (err < 0) dev_err(client->dev, "debugfs setup failed: %d\n", err); } @@ -1141,9 +1141,10 @@ static int tegra_hdmi_drm_init(struct host1x_client *client, return 0; } -static int tegra_hdmi_drm_exit(struct host1x_client *client) +static int tegra_hdmi_exit(struct host1x_client *client) { - struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); + struct tegra_drm_client *drm = to_tegra_drm_client(client); + struct tegra_hdmi *hdmi = tegra_drm_client_to_hdmi(drm); int err; if (IS_ENABLED(CONFIG_DEBUG_FS)) { @@ -1169,8 +1170,8 @@ static int tegra_hdmi_drm_exit(struct host1x_client *client) } static const struct host1x_client_ops hdmi_client_ops = { - .drm_init = tegra_hdmi_drm_init, - .drm_exit = tegra_hdmi_drm_exit, + .init = tegra_hdmi_init, + .exit = tegra_hdmi_exit, }; static int tegra_hdmi_probe(struct platform_device *pdev) @@ -1246,11 +1247,11 @@ static int tegra_hdmi_probe(struct platform_device *pdev) hdmi->irq = err; - hdmi->client.ops = &hdmi_client_ops; - INIT_LIST_HEAD(&hdmi->client.list); - hdmi->client.dev = &pdev->dev; + INIT_LIST_HEAD(&hdmi->client.base.list); + hdmi->client.base.ops = &hdmi_client_ops; + hdmi->client.base.dev = &pdev->dev; - err = host1x_register_client(tegra, &hdmi->client); + err = host1x_register_client(tegra, &hdmi->client.base); if (err < 0) { dev_err(&pdev->dev, "failed to register host1x client: %d\n", err); @@ -1268,7 +1269,7 @@ static int tegra_hdmi_remove(struct platform_device *pdev) struct tegra_hdmi *hdmi = platform_get_drvdata(pdev); int err; - err = host1x_unregister_client(tegra, &hdmi->client); + err = host1x_unregister_client(tegra, &hdmi->client.base); if (err < 0) { dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", err); diff --git a/include/linux/host1x.h b/include/linux/host1x.h index fe09939800bc..d429a938ba13 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -25,4 +25,24 @@ enum host1x_class { HOST1X_CLASS_GR2D_SB = 0x52, }; +struct host1x_client; + +struct host1x_client_ops { + int (*init)(struct host1x_client *client); + int (*exit)(struct host1x_client *client); +}; + +struct host1x_client { + struct list_head list; + struct device *dev; + + const struct host1x_client_ops *ops; + + enum host1x_class class; + struct host1x_channel *channel; + + struct host1x_syncpt **syncpts; + unsigned int num_syncpts; +}; + #endif -- cgit v1.2.3 From 35d747a81d7eb824bd0c3476cd0c564b52ad5353 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 24 Sep 2013 16:30:32 +0200 Subject: gpu: host1x: Expose syncpt and channel functionality Expose the buffer objects, syncpoint and channel functionality in the public public header so that drivers can use them. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/cdma.c | 2 +- drivers/gpu/host1x/channel.h | 6 -- drivers/gpu/host1x/drm/drm.c | 2 - drivers/gpu/host1x/drm/gem.h | 4 +- drivers/gpu/host1x/drm/gr2d.c | 6 +- drivers/gpu/host1x/drm/hdmi.c | 2 +- drivers/gpu/host1x/host1x_bo.h | 87 ----------------- drivers/gpu/host1x/hw/channel_hw.c | 1 - drivers/gpu/host1x/hw/debug_hw.c | 1 - drivers/gpu/host1x/job.c | 2 +- drivers/gpu/host1x/job.h | 108 ---------------------- drivers/gpu/host1x/syncpt.c | 19 ++++ drivers/gpu/host1x/syncpt.h | 40 +------- include/linux/host1x.h | 185 +++++++++++++++++++++++++++++++++++++ 14 files changed, 211 insertions(+), 254 deletions(-) delete mode 100644 drivers/gpu/host1x/host1x_bo.h (limited to 'include') diff --git a/drivers/gpu/host1x/cdma.c b/drivers/gpu/host1x/cdma.c index de72172d3b5f..3995255b16c7 100644 --- a/drivers/gpu/host1x/cdma.c +++ b/drivers/gpu/host1x/cdma.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,6 @@ #include "channel.h" #include "dev.h" #include "debug.h" -#include "host1x_bo.h" #include "job.h" /* diff --git a/drivers/gpu/host1x/channel.h b/drivers/gpu/host1x/channel.h index 48723b8eea42..df767cf90d51 100644 --- a/drivers/gpu/host1x/channel.h +++ b/drivers/gpu/host1x/channel.h @@ -40,12 +40,6 @@ struct host1x_channel { /* channel list operations */ int host1x_channel_list_init(struct host1x *host); -struct host1x_channel *host1x_channel_request(struct device *dev); -void host1x_channel_free(struct host1x_channel *channel); -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); - #define host1x_for_each_channel(host, channel) \ list_for_each_entry(channel, &host->chlist.list, list) diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index 33d966145293..1abcdbc7f4cd 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c @@ -8,10 +8,8 @@ */ #include "host1x_client.h" -#include "dev.h" #include "drm.h" #include "gem.h" -#include "syncpt.h" #define DRIVER_NAME "tegra" #define DRIVER_DESC "NVIDIA Tegra graphics" diff --git a/drivers/gpu/host1x/drm/gem.h b/drivers/gpu/host1x/drm/gem.h index 492533a2dacb..2b54f1425d5e 100644 --- a/drivers/gpu/host1x/drm/gem.h +++ b/drivers/gpu/host1x/drm/gem.h @@ -19,11 +19,11 @@ #ifndef __HOST1X_GEM_H #define __HOST1X_GEM_H +#include + #include #include -#include "host1x_bo.h" - struct tegra_bo { struct drm_gem_object gem; struct host1x_bo base; diff --git a/drivers/gpu/host1x/drm/gr2d.c b/drivers/gpu/host1x/drm/gr2d.c index a7edc794c5ce..dfb822428ca0 100644 --- a/drivers/gpu/host1x/drm/gr2d.c +++ b/drivers/gpu/host1x/drm/gr2d.c @@ -16,13 +16,9 @@ #include -#include "channel.h" +#include "host1x_client.h" #include "drm.h" #include "gem.h" -#include "job.h" -#include "host1x_bo.h" -#include "host1x_client.h" -#include "syncpt.h" #define GR2D_NUM_REGS 0x4d diff --git a/drivers/gpu/host1x/drm/hdmi.c b/drivers/gpu/host1x/drm/hdmi.c index 30ac9e872b0d..a2370045993c 100644 --- a/drivers/gpu/host1x/drm/hdmi.c +++ b/drivers/gpu/host1x/drm/hdmi.c @@ -13,10 +13,10 @@ #include #include +#include "host1x_client.h" #include "hdmi.h" #include "drm.h" #include "dc.h" -#include "host1x_client.h" struct tegra_hdmi { struct tegra_drm_client client; diff --git a/drivers/gpu/host1x/host1x_bo.h b/drivers/gpu/host1x/host1x_bo.h deleted file mode 100644 index 4c1f10bd773d..000000000000 --- a/drivers/gpu/host1x/host1x_bo.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Tegra host1x Memory Management Abstraction header - * - * Copyright (c) 2012-2013, NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef _HOST1X_BO_H -#define _HOST1X_BO_H - -struct host1x_bo; - -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); - void *(*mmap)(struct host1x_bo *bo); - void (*munmap)(struct host1x_bo *bo, void *addr); - void *(*kmap)(struct host1x_bo *bo, unsigned int pagenum); - void (*kunmap)(struct host1x_bo *bo, unsigned int pagenum, void *addr); -}; - -struct host1x_bo { - const struct host1x_bo_ops *ops; -}; - -static inline void host1x_bo_init(struct host1x_bo *bo, - const struct host1x_bo_ops *ops) -{ - bo->ops = ops; -} - -static inline struct host1x_bo *host1x_bo_get(struct host1x_bo *bo) -{ - return bo->ops->get(bo); -} - -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) -{ - return bo->ops->pin(bo, sgt); -} - -static inline void host1x_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt) -{ - bo->ops->unpin(bo, sgt); -} - -static inline void *host1x_bo_mmap(struct host1x_bo *bo) -{ - return bo->ops->mmap(bo); -} - -static inline void host1x_bo_munmap(struct host1x_bo *bo, void *addr) -{ - bo->ops->munmap(bo, addr); -} - -static inline void *host1x_bo_kmap(struct host1x_bo *bo, unsigned int pagenum) -{ - return bo->ops->kmap(bo, pagenum); -} - -static inline void host1x_bo_kunmap(struct host1x_bo *bo, - unsigned int pagenum, void *addr) -{ - bo->ops->kunmap(bo, pagenum, addr); -} - -#endif diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c index c950bc655ade..aa9bdf331139 100644 --- a/drivers/gpu/host1x/hw/channel_hw.c +++ b/drivers/gpu/host1x/hw/channel_hw.c @@ -21,7 +21,6 @@ #include -#include "host1x_bo.h" #include "channel.h" #include "dev.h" #include "intr.h" diff --git a/drivers/gpu/host1x/hw/debug_hw.c b/drivers/gpu/host1x/hw/debug_hw.c index dfad66312286..788fe7179d8f 100644 --- a/drivers/gpu/host1x/hw/debug_hw.c +++ b/drivers/gpu/host1x/hw/debug_hw.c @@ -19,7 +19,6 @@ #include "debug.h" #include "cdma.h" #include "channel.h" -#include "host1x_bo.h" #define HOST1X_DEBUG_MAX_PAGE_OFFSET 102400 diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c index d1b72f4c744d..de5ec333ce1a 100644 --- a/drivers/gpu/host1x/job.c +++ b/drivers/gpu/host1x/job.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -27,7 +28,6 @@ #include "channel.h" #include "dev.h" -#include "host1x_bo.h" #include "job.h" #include "syncpt.h" diff --git a/drivers/gpu/host1x/job.h b/drivers/gpu/host1x/job.h index fba45f20458e..33a697d6dcef 100644 --- a/drivers/gpu/host1x/job.h +++ b/drivers/gpu/host1x/job.h @@ -34,15 +34,6 @@ struct host1x_cmdbuf { u32 pad; }; -struct host1x_reloc { - struct host1x_bo *cmdbuf; - u32 cmdbuf_offset; - struct host1x_bo *target; - u32 target_offset; - u32 shift; - u32 pad; -}; - struct host1x_waitchk { struct host1x_bo *bo; u32 offset; @@ -55,105 +46,6 @@ struct host1x_job_unpin_data { struct sg_table *sgt; }; -/* - * Each submit is tracked as a host1x_job. - */ -struct host1x_job { - /* When refcount goes to zero, job can be freed */ - struct kref ref; - - /* List entry */ - struct list_head list; - - /* Channel where job is submitted to */ - struct host1x_channel *channel; - - u32 client; - - /* Gathers and their memory */ - struct host1x_job_gather *gathers; - unsigned int num_gathers; - - /* Wait checks to be processed at submit time */ - struct host1x_waitchk *waitchk; - unsigned int num_waitchk; - u32 waitchk_mask; - - /* Array of handles to be pinned & unpinned */ - struct host1x_reloc *relocarray; - unsigned int num_relocs; - struct host1x_job_unpin_data *unpins; - unsigned int num_unpins; - - dma_addr_t *addr_phys; - dma_addr_t *gather_addr_phys; - dma_addr_t *reloc_addr_phys; - - /* Sync point id, number of increments and end related to the submit */ - u32 syncpt_id; - u32 syncpt_incrs; - u32 syncpt_end; - - /* Maximum time to wait for this job */ - unsigned int timeout; - - /* Index and number of slots used in the push buffer */ - unsigned int first_get; - unsigned int num_slots; - - /* Copy of gathers */ - size_t gather_copy_size; - dma_addr_t gather_copy; - u8 *gather_copy_mapped; - - /* Check if register is marked as an address reg */ - int (*is_addr_reg)(struct device *dev, u32 reg, u32 class); - - /* Request a SETCLASS to this class */ - u32 class; - - /* Add a channel wait for previous ops to complete */ - bool serialize; -}; -/* - * Allocate memory for a job. Just enough memory will be allocated to - * accomodate the submit. - */ -struct host1x_job *host1x_job_alloc(struct host1x_channel *ch, - u32 num_cmdbufs, u32 num_relocs, - u32 num_waitchks); - -/* - * Add a gather to a job. - */ -void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *mem_id, - u32 words, u32 offset); - -/* - * Increment reference going to host1x_job. - */ -struct host1x_job *host1x_job_get(struct host1x_job *job); - -/* - * Decrement reference job, free if goes to zero. - */ -void host1x_job_put(struct host1x_job *job); - -/* - * Pin memory related to job. This handles relocation of addresses to the - * host1x address space. Handles both the gather memory and any other memory - * referred to from the gather buffers. - * - * Handles also patching out host waits that would wait for an expired sync - * point value. - */ -int host1x_job_pin(struct host1x_job *job, struct device *dev); - -/* - * Unpin memory related to job. - */ -void host1x_job_unpin(struct host1x_job *job); - /* * Dump contents of job to debug output. */ diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c index 409745b949db..03cf2922e469 100644 --- a/drivers/gpu/host1x/syncpt.c +++ b/drivers/gpu/host1x/syncpt.c @@ -354,6 +354,25 @@ void host1x_syncpt_deinit(struct host1x *host) kfree(sp->name); } +/* + * Read max. It indicates how many operations there are in queue, either in + * channel or in a software thread. + * */ +u32 host1x_syncpt_read_max(struct host1x_syncpt *sp) +{ + smp_rmb(); + return (u32)atomic_read(&sp->max_val); +} + +/* + * Read min, which is a shadow of the current sync point value in hardware. + */ +u32 host1x_syncpt_read_min(struct host1x_syncpt *sp) +{ + smp_rmb(); + return (u32)atomic_read(&sp->min_val); +} + int host1x_syncpt_nb_pts(struct host1x *host) { return host->info->nb_pts; diff --git a/drivers/gpu/host1x/syncpt.h b/drivers/gpu/host1x/syncpt.h index 267c0b9d3647..4eb933a497fd 100644 --- a/drivers/gpu/host1x/syncpt.h +++ b/drivers/gpu/host1x/syncpt.h @@ -20,6 +20,7 @@ #define __HOST1X_SYNCPT_H #include +#include #include #include @@ -50,25 +51,6 @@ int host1x_syncpt_init(struct host1x *host); /* Free sync point array */ void host1x_syncpt_deinit(struct host1x *host); -/* - * Read max. It indicates how many operations there are in queue, either in - * channel or in a software thread. - * */ -static inline u32 host1x_syncpt_read_max(struct host1x_syncpt *sp) -{ - smp_rmb(); - return (u32)atomic_read(&sp->max_val); -} - -/* - * Read min, which is a shadow of the current sync point value in hardware. - */ -static inline u32 host1x_syncpt_read_min(struct host1x_syncpt *sp) -{ - smp_rmb(); - return (u32)atomic_read(&sp->min_val); -} - /* Return number of sync point supported. */ int host1x_syncpt_nb_pts(struct host1x *host); @@ -112,9 +94,6 @@ static inline bool host1x_syncpt_idle(struct host1x_syncpt *sp) return (min == max); } -/* Return pointer to struct denoting sync point id. */ -struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id); - /* Load current value from hardware to the shadow register. */ u32 host1x_syncpt_load(struct host1x_syncpt *sp); @@ -130,16 +109,9 @@ void host1x_syncpt_restore(struct host1x *host); /* Read current wait base value into shadow register and return it. */ u32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp); -/* Request incrementing a sync point. */ -int host1x_syncpt_incr(struct host1x_syncpt *sp); - /* Indicate future operations by incrementing the sync point max. */ u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs); -/* Wait until sync point reaches a threshold value, or a timeout. */ -int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, - long timeout, u32 *value); - /* Check if sync point id is valid. */ static inline int host1x_syncpt_is_valid(struct host1x_syncpt *sp) { @@ -149,14 +121,4 @@ static inline int host1x_syncpt_is_valid(struct host1x_syncpt *sp) /* Patch a wait by replacing it with a wait for syncpt 0 value 0 */ int host1x_syncpt_patch_wait(struct host1x_syncpt *sp, void *patch_addr); -/* Return id of the sync point */ -u32 host1x_syncpt_id(struct host1x_syncpt *sp); - -/* Allocate a sync point for a device. */ -struct host1x_syncpt *host1x_syncpt_request(struct device *dev, - bool client_managed); - -/* Free a sync point. */ -void host1x_syncpt_free(struct host1x_syncpt *sp); - #endif diff --git a/include/linux/host1x.h b/include/linux/host1x.h index d429a938ba13..7442f2a57039 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -19,6 +19,9 @@ #ifndef __LINUX_HOST1X_H #define __LINUX_HOST1X_H +#include +#include + enum host1x_class { HOST1X_CLASS_HOST1X = 0x1, HOST1X_CLASS_GR2D = 0x51, @@ -45,4 +48,186 @@ struct host1x_client { unsigned int num_syncpts; }; +/* + * host1x buffer objects + */ + +struct host1x_bo; +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); + void *(*mmap)(struct host1x_bo *bo); + void (*munmap)(struct host1x_bo *bo, void *addr); + void *(*kmap)(struct host1x_bo *bo, unsigned int pagenum); + void (*kunmap)(struct host1x_bo *bo, unsigned int pagenum, void *addr); +}; + +struct host1x_bo { + const struct host1x_bo_ops *ops; +}; + +static inline void host1x_bo_init(struct host1x_bo *bo, + const struct host1x_bo_ops *ops) +{ + bo->ops = ops; +} + +static inline struct host1x_bo *host1x_bo_get(struct host1x_bo *bo) +{ + return bo->ops->get(bo); +} + +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) +{ + return bo->ops->pin(bo, sgt); +} + +static inline void host1x_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt) +{ + bo->ops->unpin(bo, sgt); +} + +static inline void *host1x_bo_mmap(struct host1x_bo *bo) +{ + return bo->ops->mmap(bo); +} + +static inline void host1x_bo_munmap(struct host1x_bo *bo, void *addr) +{ + bo->ops->munmap(bo, addr); +} + +static inline void *host1x_bo_kmap(struct host1x_bo *bo, unsigned int pagenum) +{ + return bo->ops->kmap(bo, pagenum); +} + +static inline void host1x_bo_kunmap(struct host1x_bo *bo, + unsigned int pagenum, void *addr) +{ + bo->ops->kunmap(bo, pagenum, addr); +} + +/* + * host1x syncpoints + */ + +struct host1x_syncpt; +struct host1x; + +struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id); +u32 host1x_syncpt_id(struct host1x_syncpt *sp); +u32 host1x_syncpt_read_min(struct host1x_syncpt *sp); +u32 host1x_syncpt_read_max(struct host1x_syncpt *sp); +int host1x_syncpt_incr(struct host1x_syncpt *sp); +int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, + u32 *value); +struct host1x_syncpt *host1x_syncpt_request(struct device *dev, + bool client_managed); +void host1x_syncpt_free(struct host1x_syncpt *sp); + +/* + * host1x channel + */ + +struct host1x_channel; +struct host1x_job; + +struct host1x_channel *host1x_channel_request(struct device *dev); +void host1x_channel_free(struct host1x_channel *channel); +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); + +/* + * host1x job + */ + +struct host1x_reloc { + struct host1x_bo *cmdbuf; + u32 cmdbuf_offset; + struct host1x_bo *target; + u32 target_offset; + u32 shift; + u32 pad; +}; + +struct host1x_job { + /* When refcount goes to zero, job can be freed */ + struct kref ref; + + /* List entry */ + struct list_head list; + + /* Channel where job is submitted to */ + struct host1x_channel *channel; + + u32 client; + + /* Gathers and their memory */ + struct host1x_job_gather *gathers; + unsigned int num_gathers; + + /* Wait checks to be processed at submit time */ + struct host1x_waitchk *waitchk; + unsigned int num_waitchk; + u32 waitchk_mask; + + /* Array of handles to be pinned & unpinned */ + struct host1x_reloc *relocarray; + unsigned int num_relocs; + struct host1x_job_unpin_data *unpins; + unsigned int num_unpins; + + dma_addr_t *addr_phys; + dma_addr_t *gather_addr_phys; + dma_addr_t *reloc_addr_phys; + + /* Sync point id, number of increments and end related to the submit */ + u32 syncpt_id; + u32 syncpt_incrs; + u32 syncpt_end; + + /* Maximum time to wait for this job */ + unsigned int timeout; + + /* Index and number of slots used in the push buffer */ + unsigned int first_get; + unsigned int num_slots; + + /* Copy of gathers */ + size_t gather_copy_size; + dma_addr_t gather_copy; + u8 *gather_copy_mapped; + + /* Check if register is marked as an address reg */ + int (*is_addr_reg)(struct device *dev, u32 reg, u32 class); + + /* Request a SETCLASS to this class */ + u32 class; + + /* Add a channel wait for previous ops to complete */ + bool serialize; +}; + +struct host1x_job *host1x_job_alloc(struct host1x_channel *ch, + u32 num_cmdbufs, u32 num_relocs, + u32 num_waitchks); +void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *mem_id, + u32 words, u32 offset); +struct host1x_job *host1x_job_get(struct host1x_job *job); +void host1x_job_put(struct host1x_job *job); +int host1x_job_pin(struct host1x_job *job, struct device *dev); +void host1x_job_unpin(struct host1x_job *job); + #endif -- cgit v1.2.3 From 776dc38403676f499a73d32e2e7c61eb5b42f736 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 14 Oct 2013 14:43:22 +0200 Subject: drm/tegra: Move subdevice infrastructure to host1x The Tegra DRM driver currently uses some infrastructure to defer the DRM core initialization until all required devices have registered. The same infrastructure can potentially be used by any other driver that requires more than a single sub-device of the host1x module. Make the infrastructure more generic and keep only the DRM specific code in the DRM part of the driver. Eventually this will make it easy to move the DRM driver part back to the DRM subsystem. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/Makefile | 2 + drivers/gpu/host1x/bus.c | 550 +++++++++++++++++++++++++++++++++++++ drivers/gpu/host1x/bus.h | 29 ++ drivers/gpu/host1x/dev.c | 60 ++-- drivers/gpu/host1x/dev.h | 9 +- drivers/gpu/host1x/drm/bus.c | 76 +++++ drivers/gpu/host1x/drm/dc.c | 30 +- drivers/gpu/host1x/drm/drm.c | 347 ++++++++--------------- drivers/gpu/host1x/drm/drm.h | 31 ++- drivers/gpu/host1x/drm/gr2d.c | 60 ++-- drivers/gpu/host1x/drm/hdmi.c | 28 +- drivers/gpu/host1x/host1x_client.h | 35 --- include/drm/drmP.h | 1 + include/linux/host1x.h | 45 ++- 14 files changed, 918 insertions(+), 385 deletions(-) create mode 100644 drivers/gpu/host1x/bus.c create mode 100644 drivers/gpu/host1x/bus.h create mode 100644 drivers/gpu/host1x/drm/bus.c delete mode 100644 drivers/gpu/host1x/host1x_client.h (limited to 'include') diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile index 3b037b6e0298..7b781920c58a 100644 --- a/drivers/gpu/host1x/Makefile +++ b/drivers/gpu/host1x/Makefile @@ -1,6 +1,7 @@ ccflags-y = -Idrivers/gpu/host1x host1x-y = \ + bus.o \ syncpt.o \ dev.o \ intr.o \ @@ -17,4 +18,5 @@ host1x-$(CONFIG_DRM_TEGRA) += drm/drm.o drm/fb.o drm/dc.o host1x-$(CONFIG_DRM_TEGRA) += drm/output.o drm/rgb.o drm/hdmi.o host1x-$(CONFIG_DRM_TEGRA) += drm/gem.o host1x-$(CONFIG_DRM_TEGRA) += drm/gr2d.o +host1x-$(CONFIG_DRM_TEGRA) += drm/bus.o obj-$(CONFIG_TEGRA_HOST1X) += host1x.o diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c new file mode 100644 index 000000000000..509383f8be03 --- /dev/null +++ b/drivers/gpu/host1x/bus.c @@ -0,0 +1,550 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * Copyright (C) 2012-2013, NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "dev.h" + +static DEFINE_MUTEX(clients_lock); +static LIST_HEAD(clients); + +static DEFINE_MUTEX(drivers_lock); +static LIST_HEAD(drivers); + +static DEFINE_MUTEX(devices_lock); +static LIST_HEAD(devices); + +struct host1x_subdev { + struct host1x_client *client; + struct device_node *np; + struct list_head list; +}; + +/** + * host1x_subdev_add() - add a new subdevice with an associated device node + */ +static int host1x_subdev_add(struct host1x_device *device, + struct device_node *np) +{ + struct host1x_subdev *subdev; + + subdev = kzalloc(sizeof(*subdev), GFP_KERNEL); + if (!subdev) + return -ENOMEM; + + INIT_LIST_HEAD(&subdev->list); + subdev->np = of_node_get(np); + + mutex_lock(&device->subdevs_lock); + list_add_tail(&subdev->list, &device->subdevs); + mutex_unlock(&device->subdevs_lock); + + return 0; +} + +/** + * host1x_subdev_del() - remove subdevice + */ +static void host1x_subdev_del(struct host1x_subdev *subdev) +{ + list_del(&subdev->list); + of_node_put(subdev->np); + kfree(subdev); +} + +/** + * host1x_device_parse_dt() - scan device tree and add matching subdevices + */ +static int host1x_device_parse_dt(struct host1x_device *device) +{ + struct device_node *np; + int err; + + for_each_child_of_node(device->dev.parent->of_node, np) { + if (of_match_node(device->driver->subdevs, np) && + of_device_is_available(np)) { + err = host1x_subdev_add(device, np); + if (err < 0) + return err; + } + } + + return 0; +} + +static void host1x_subdev_register(struct host1x_device *device, + struct host1x_subdev *subdev, + struct host1x_client *client) +{ + int err; + + /* + * Move the subdevice to the list of active (registered) subdevices + * and associate it with a client. At the same time, associate the + * client with its parent device. + */ + mutex_lock(&device->subdevs_lock); + mutex_lock(&device->clients_lock); + list_move_tail(&client->list, &device->clients); + list_move_tail(&subdev->list, &device->active); + client->parent = &device->dev; + subdev->client = client; + mutex_unlock(&device->clients_lock); + mutex_unlock(&device->subdevs_lock); + + /* + * When all subdevices have been registered, the composite device is + * ready to be probed. + */ + if (list_empty(&device->subdevs)) { + err = device->driver->probe(device); + if (err < 0) + dev_err(&device->dev, "probe failed: %d\n", err); + } +} + +static void __host1x_subdev_unregister(struct host1x_device *device, + struct host1x_subdev *subdev) +{ + struct host1x_client *client = subdev->client; + int err; + + /* + * If all subdevices have been activated, we're about to remove the + * first active subdevice, so unload the driver first. + */ + if (list_empty(&device->subdevs)) { + err = device->driver->remove(device); + if (err < 0) + dev_err(&device->dev, "remove failed: %d\n", err); + } + + /* + * Move the subdevice back to the list of idle subdevices and remove + * it from list of clients. + */ + mutex_lock(&device->clients_lock); + subdev->client = NULL; + client->parent = NULL; + list_move_tail(&subdev->list, &device->subdevs); + /* + * XXX: Perhaps don't do this here, but rather explicitly remove it + * when the device is about to be deleted. + * + * This is somewhat complicated by the fact that this function is + * used to remove the subdevice when a client is unregistered but + * also when the composite device is about to be removed. + */ + list_del_init(&client->list); + mutex_unlock(&device->clients_lock); +} + +static void host1x_subdev_unregister(struct host1x_device *device, + struct host1x_subdev *subdev) +{ + mutex_lock(&device->subdevs_lock); + __host1x_subdev_unregister(device, subdev); + mutex_unlock(&device->subdevs_lock); +} + +int host1x_device_init(struct host1x_device *device) +{ + struct host1x_client *client; + int err; + + mutex_lock(&device->clients_lock); + + list_for_each_entry(client, &device->clients, list) { + if (client->ops && client->ops->init) { + err = client->ops->init(client); + if (err < 0) { + dev_err(&device->dev, + "failed to initialize %s: %d\n", + dev_name(client->dev), err); + mutex_unlock(&device->clients_lock); + return err; + } + } + } + + mutex_unlock(&device->clients_lock); + + return 0; +} + +int host1x_device_exit(struct host1x_device *device) +{ + struct host1x_client *client; + int err; + + mutex_lock(&device->clients_lock); + + list_for_each_entry_reverse(client, &device->clients, list) { + if (client->ops && client->ops->exit) { + err = client->ops->exit(client); + if (err < 0) { + dev_err(&device->dev, + "failed to cleanup %s: %d\n", + dev_name(client->dev), err); + mutex_unlock(&device->clients_lock); + return err; + } + } + } + + mutex_unlock(&device->clients_lock); + + return 0; +} + +static int host1x_register_client(struct host1x *host1x, + struct host1x_client *client) +{ + struct host1x_device *device; + struct host1x_subdev *subdev; + + mutex_lock(&host1x->devices_lock); + + list_for_each_entry(device, &host1x->devices, list) { + list_for_each_entry(subdev, &device->subdevs, list) { + if (subdev->np == client->dev->of_node) { + host1x_subdev_register(device, subdev, client); + mutex_unlock(&host1x->devices_lock); + return 0; + } + } + } + + mutex_unlock(&host1x->devices_lock); + return -ENODEV; +} + +static int host1x_unregister_client(struct host1x *host1x, + struct host1x_client *client) +{ + struct host1x_device *device, *dt; + struct host1x_subdev *subdev; + + mutex_lock(&host1x->devices_lock); + + list_for_each_entry_safe(device, dt, &host1x->devices, list) { + list_for_each_entry(subdev, &device->active, list) { + if (subdev->client == client) { + host1x_subdev_unregister(device, subdev); + mutex_unlock(&host1x->devices_lock); + return 0; + } + } + } + + mutex_unlock(&host1x->devices_lock); + return -ENODEV; +} + +struct bus_type host1x_bus_type = { + .name = "host1x", +}; + +int host1x_bus_init(void) +{ + return bus_register(&host1x_bus_type); +} + +void host1x_bus_exit(void) +{ + bus_unregister(&host1x_bus_type); +} + +static void host1x_device_release(struct device *dev) +{ + struct host1x_device *device = to_host1x_device(dev); + + kfree(device); +} + +static int host1x_device_add(struct host1x *host1x, + struct host1x_driver *driver) +{ + struct host1x_client *client, *tmp; + struct host1x_subdev *subdev; + struct host1x_device *device; + int err; + + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (!device) + return -ENOMEM; + + mutex_init(&device->subdevs_lock); + INIT_LIST_HEAD(&device->subdevs); + INIT_LIST_HEAD(&device->active); + mutex_init(&device->clients_lock); + INIT_LIST_HEAD(&device->clients); + INIT_LIST_HEAD(&device->list); + device->driver = driver; + + device->dev.coherent_dma_mask = host1x->dev->coherent_dma_mask; + device->dev.dma_mask = &device->dev.coherent_dma_mask; + device->dev.release = host1x_device_release; + dev_set_name(&device->dev, driver->name); + device->dev.bus = &host1x_bus_type; + device->dev.parent = host1x->dev; + + err = device_register(&device->dev); + if (err < 0) + return err; + + err = host1x_device_parse_dt(device); + if (err < 0) { + device_unregister(&device->dev); + return err; + } + + mutex_lock(&host1x->devices_lock); + list_add_tail(&device->list, &host1x->devices); + mutex_unlock(&host1x->devices_lock); + + mutex_lock(&clients_lock); + + list_for_each_entry_safe(client, tmp, &clients, list) { + list_for_each_entry(subdev, &device->subdevs, list) { + if (subdev->np == client->dev->of_node) { + host1x_subdev_register(device, subdev, client); + break; + } + } + } + + mutex_unlock(&clients_lock); + + return 0; +} + +/* + * Removes a device by first unregistering any subdevices and then removing + * itself from the list of devices. + * + * This function must be called with the host1x->devices_lock held. + */ +static void host1x_device_del(struct host1x *host1x, + struct host1x_device *device) +{ + struct host1x_subdev *subdev, *sd; + struct host1x_client *client, *cl; + + mutex_lock(&device->subdevs_lock); + + /* unregister subdevices */ + list_for_each_entry_safe(subdev, sd, &device->active, list) { + /* + * host1x_subdev_unregister() will remove the client from + * any lists, so we'll need to manually add it back to the + * list of idle clients. + * + * XXX: Alternatively, perhaps don't remove the client from + * any lists in host1x_subdev_unregister() and instead do + * that explicitly from host1x_unregister_client()? + */ + client = subdev->client; + + __host1x_subdev_unregister(device, subdev); + + /* add the client to the list of idle clients */ + mutex_lock(&clients_lock); + list_add_tail(&client->list, &clients); + mutex_unlock(&clients_lock); + } + + /* remove subdevices */ + list_for_each_entry_safe(subdev, sd, &device->subdevs, list) + host1x_subdev_del(subdev); + + mutex_unlock(&device->subdevs_lock); + + /* move clients to idle list */ + mutex_lock(&clients_lock); + mutex_lock(&device->clients_lock); + + list_for_each_entry_safe(client, cl, &device->clients, list) + list_move_tail(&client->list, &clients); + + mutex_unlock(&device->clients_lock); + mutex_unlock(&clients_lock); + + /* finally remove the device */ + list_del_init(&device->list); + device_unregister(&device->dev); +} + +static void host1x_attach_driver(struct host1x *host1x, + struct host1x_driver *driver) +{ + struct host1x_device *device; + int err; + + mutex_lock(&host1x->devices_lock); + + list_for_each_entry(device, &host1x->devices, list) { + if (device->driver == driver) { + mutex_unlock(&host1x->devices_lock); + return; + } + } + + mutex_unlock(&host1x->devices_lock); + + err = host1x_device_add(host1x, driver); + if (err < 0) + dev_err(host1x->dev, "failed to allocate device: %d\n", err); +} + +static void host1x_detach_driver(struct host1x *host1x, + struct host1x_driver *driver) +{ + struct host1x_device *device, *tmp; + + mutex_lock(&host1x->devices_lock); + + list_for_each_entry_safe(device, tmp, &host1x->devices, list) + if (device->driver == driver) + host1x_device_del(host1x, device); + + mutex_unlock(&host1x->devices_lock); +} + +int host1x_register(struct host1x *host1x) +{ + struct host1x_driver *driver; + + mutex_lock(&devices_lock); + list_add_tail(&host1x->list, &devices); + mutex_unlock(&devices_lock); + + mutex_lock(&drivers_lock); + + list_for_each_entry(driver, &drivers, list) + host1x_attach_driver(host1x, driver); + + mutex_unlock(&drivers_lock); + + return 0; +} + +int host1x_unregister(struct host1x *host1x) +{ + struct host1x_driver *driver; + + mutex_lock(&drivers_lock); + + list_for_each_entry(driver, &drivers, list) + host1x_detach_driver(host1x, driver); + + mutex_unlock(&drivers_lock); + + mutex_lock(&devices_lock); + list_del_init(&host1x->list); + mutex_unlock(&devices_lock); + + return 0; +} + +int host1x_driver_register(struct host1x_driver *driver) +{ + struct host1x *host1x; + + INIT_LIST_HEAD(&driver->list); + + mutex_lock(&drivers_lock); + list_add_tail(&driver->list, &drivers); + mutex_unlock(&drivers_lock); + + mutex_lock(&devices_lock); + + list_for_each_entry(host1x, &devices, list) + host1x_attach_driver(host1x, driver); + + mutex_unlock(&devices_lock); + + return 0; +} +EXPORT_SYMBOL(host1x_driver_register); + +void host1x_driver_unregister(struct host1x_driver *driver) +{ + mutex_lock(&drivers_lock); + list_del_init(&driver->list); + mutex_unlock(&drivers_lock); +} +EXPORT_SYMBOL(host1x_driver_unregister); + +int host1x_client_register(struct host1x_client *client) +{ + struct host1x *host1x; + int err; + + mutex_lock(&devices_lock); + + list_for_each_entry(host1x, &devices, list) { + err = host1x_register_client(host1x, client); + if (!err) { + mutex_unlock(&devices_lock); + return 0; + } + } + + mutex_unlock(&devices_lock); + + mutex_lock(&clients_lock); + list_add_tail(&client->list, &clients); + mutex_unlock(&clients_lock); + + return 0; +} +EXPORT_SYMBOL(host1x_client_register); + +int host1x_client_unregister(struct host1x_client *client) +{ + struct host1x_client *c; + struct host1x *host1x; + int err; + + mutex_lock(&devices_lock); + + list_for_each_entry(host1x, &devices, list) { + err = host1x_unregister_client(host1x, client); + if (!err) { + mutex_unlock(&devices_lock); + return 0; + } + } + + mutex_unlock(&devices_lock); + mutex_lock(&clients_lock); + + list_for_each_entry(c, &clients, list) { + if (c == client) { + list_del_init(&c->list); + break; + } + } + + mutex_unlock(&clients_lock); + + return 0; +} +EXPORT_SYMBOL(host1x_client_unregister); diff --git a/drivers/gpu/host1x/bus.h b/drivers/gpu/host1x/bus.h new file mode 100644 index 000000000000..4099e99212c8 --- /dev/null +++ b/drivers/gpu/host1x/bus.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * Copyright (C) 2012-2013, NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HOST1X_BUS_H +#define HOST1X_BUS_H + +struct host1x; + +int host1x_bus_init(void); +void host1x_bus_exit(void); + +int host1x_register(struct host1x *host1x); +int host1x_unregister(struct host1x *host1x); + +#endif diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 105aa4ed665a..de0fd552710d 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -27,24 +27,12 @@ #define CREATE_TRACE_POINTS #include +#include "bus.h" #include "dev.h" #include "intr.h" #include "channel.h" #include "debug.h" #include "hw/host1x01.h" -#include "host1x_client.h" - -void host1x_set_drm_data(struct device *dev, void *data) -{ - struct host1x *host1x = dev_get_drvdata(dev); - host1x->drm_data = data; -} - -void *host1x_get_drm_data(struct device *dev) -{ - struct host1x *host1x = dev_get_drvdata(dev); - return host1x ? host1x->drm_data : NULL; -} void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r) { @@ -114,6 +102,9 @@ static int host1x_probe(struct platform_device *pdev) if (!host) return -ENOMEM; + mutex_init(&host->devices_lock); + INIT_LIST_HEAD(&host->devices); + INIT_LIST_HEAD(&host->list); host->dev = &pdev->dev; host->info = id->data; @@ -163,10 +154,14 @@ static int host1x_probe(struct platform_device *pdev) host1x_debug_init(host); - tegra_drm_alloc(pdev); + err = host1x_register(host); + if (err < 0) + goto fail_deinit_intr; return 0; +fail_deinit_intr: + host1x_intr_deinit(host); fail_deinit_syncpt: host1x_syncpt_deinit(host); return err; @@ -176,6 +171,7 @@ static int host1x_remove(struct platform_device *pdev) { struct host1x *host = platform_get_drvdata(pdev); + host1x_unregister(host); host1x_intr_deinit(host); host1x_syncpt_deinit(host); clk_disable_unprepare(host->clk); @@ -196,46 +192,24 @@ static int __init tegra_host1x_init(void) { int err; - err = platform_driver_register(&tegra_host1x_driver); + err = host1x_bus_init(); if (err < 0) return err; -#ifdef CONFIG_DRM_TEGRA - err = platform_driver_register(&tegra_dc_driver); - if (err < 0) - goto unregister_host1x; - - err = platform_driver_register(&tegra_hdmi_driver); - if (err < 0) - goto unregister_dc; - - err = platform_driver_register(&tegra_gr2d_driver); - if (err < 0) - goto unregister_hdmi; -#endif + err = platform_driver_register(&tegra_host1x_driver); + if (err < 0) { + host1x_bus_exit(); + return err; + } return 0; - -#ifdef CONFIG_DRM_TEGRA -unregister_hdmi: - platform_driver_unregister(&tegra_hdmi_driver); -unregister_dc: - platform_driver_unregister(&tegra_dc_driver); -unregister_host1x: - platform_driver_unregister(&tegra_host1x_driver); - return err; -#endif } module_init(tegra_host1x_init); static void __exit tegra_host1x_exit(void) { -#ifdef CONFIG_DRM_TEGRA - platform_driver_unregister(&tegra_gr2d_driver); - platform_driver_unregister(&tegra_hdmi_driver); - platform_driver_unregister(&tegra_dc_driver); -#endif platform_driver_unregister(&tegra_host1x_driver); + host1x_bus_exit(); } module_exit(tegra_host1x_exit); diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h index bed90a8131be..6cf689b9e17b 100644 --- a/drivers/gpu/host1x/dev.h +++ b/drivers/gpu/host1x/dev.h @@ -125,7 +125,10 @@ struct host1x { struct dentry *debugfs; - void *drm_data; + struct mutex devices_lock; + struct list_head devices; + + struct list_head list; }; void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v); @@ -301,8 +304,4 @@ static inline void host1x_hw_show_mlocks(struct host1x *host, struct output *o) host->debug_op->show_mlocks(host, o); } -extern struct platform_driver tegra_dc_driver; -extern struct platform_driver tegra_hdmi_driver; -extern struct platform_driver tegra_gr2d_driver; - #endif diff --git a/drivers/gpu/host1x/drm/bus.c b/drivers/gpu/host1x/drm/bus.c new file mode 100644 index 000000000000..565f8f7b9a47 --- /dev/null +++ b/drivers/gpu/host1x/drm/bus.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "drm.h" + +static int drm_host1x_set_busid(struct drm_device *dev, + struct drm_master *master) +{ + const char *device = dev_name(dev->dev); + const char *driver = dev->driver->name; + const char *bus = dev->dev->bus->name; + int length; + + master->unique_len = strlen(bus) + 1 + strlen(device); + master->unique_size = master->unique_len; + + master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL); + if (!master->unique) + return -ENOMEM; + + snprintf(master->unique, master->unique_len + 1, "%s:%s", bus, device); + + length = strlen(driver) + 1 + master->unique_len; + + dev->devname = kmalloc(length + 1, GFP_KERNEL); + if (!dev->devname) + return -ENOMEM; + + snprintf(dev->devname, length + 1, "%s@%s", driver, master->unique); + + return 0; +} + +static struct drm_bus drm_host1x_bus = { + .bus_type = DRIVER_BUS_HOST1X, + .set_busid = drm_host1x_set_busid, +}; + +int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device) +{ + struct drm_device *drm; + int ret; + + INIT_LIST_HEAD(&driver->device_list); + driver->bus = &drm_host1x_bus; + + drm = drm_dev_alloc(driver, &device->dev); + if (!drm) + return -ENOMEM; + + ret = drm_dev_register(drm, 0); + if (ret) + goto err_free; + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, + driver->major, driver->minor, driver->patchlevel, + driver->date, drm->primary->index); + + return 0; + +err_free: + drm_dev_free(drm); + return ret; +} + +void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device) +{ + struct tegra_drm *tegra = dev_get_drvdata(&device->dev); + + drm_put_dev(tegra->drm); +} diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c index 5106df08f046..588d4ba0d8cf 100644 --- a/drivers/gpu/host1x/drm/dc.c +++ b/drivers/gpu/host1x/drm/dc.c @@ -11,7 +11,6 @@ #include #include -#include "host1x_client.h" #include "dc.h" #include "drm.h" #include "gem.h" @@ -1040,28 +1039,28 @@ static int tegra_dc_debugfs_exit(struct tegra_dc *dc) static int tegra_dc_init(struct host1x_client *client) { - struct tegra_drm_client *drm = to_tegra_drm_client(client); - struct tegra_dc *dc = tegra_drm_client_to_dc(drm); + struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct tegra_dc *dc = host1x_client_to_dc(client); int err; - dc->pipe = drm->drm->mode_config.num_crtc; + dc->pipe = tegra->drm->mode_config.num_crtc; - drm_crtc_init(drm->drm, &dc->base, &tegra_crtc_funcs); + drm_crtc_init(tegra->drm, &dc->base, &tegra_crtc_funcs); drm_mode_crtc_set_gamma_size(&dc->base, 256); drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); - err = tegra_dc_rgb_init(drm->drm, dc); + err = tegra_dc_rgb_init(tegra->drm, dc); if (err < 0 && err != -ENODEV) { dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); return err; } - err = tegra_dc_add_planes(drm->drm, dc); + err = tegra_dc_add_planes(tegra->drm, dc); if (err < 0) return err; if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dc_debugfs_init(dc, drm->drm->primary); + err = tegra_dc_debugfs_init(dc, tegra->drm->primary); if (err < 0) dev_err(dc->dev, "debugfs setup failed: %d\n", err); } @@ -1079,8 +1078,7 @@ static int tegra_dc_init(struct host1x_client *client) static int tegra_dc_exit(struct host1x_client *client) { - struct tegra_drm_client *drm = to_tegra_drm_client(client); - struct tegra_dc *dc = tegra_drm_client_to_dc(drm); + struct tegra_dc *dc = host1x_client_to_dc(client); int err; devm_free_irq(dc->dev, dc->irq, dc); @@ -1107,7 +1105,6 @@ static const struct host1x_client_ops dc_client_ops = { static int tegra_dc_probe(struct platform_device *pdev) { - struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent); struct resource *regs; struct tegra_dc *dc; int err; @@ -1141,9 +1138,9 @@ static int tegra_dc_probe(struct platform_device *pdev) return -ENXIO; } - INIT_LIST_HEAD(&dc->client.base.list); - dc->client.base.ops = &dc_client_ops; - dc->client.base.dev = &pdev->dev; + INIT_LIST_HEAD(&dc->client.list); + dc->client.ops = &dc_client_ops; + dc->client.dev = &pdev->dev; err = tegra_dc_rgb_probe(dc); if (err < 0 && err != -ENODEV) { @@ -1151,7 +1148,7 @@ static int tegra_dc_probe(struct platform_device *pdev) return err; } - err = host1x_register_client(tegra, &dc->client.base); + err = host1x_client_register(&dc->client); if (err < 0) { dev_err(&pdev->dev, "failed to register host1x client: %d\n", err); @@ -1165,11 +1162,10 @@ static int tegra_dc_probe(struct platform_device *pdev) static int tegra_dc_remove(struct platform_device *pdev) { - struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent); struct tegra_dc *dc = platform_get_drvdata(pdev); int err; - err = host1x_unregister_client(tegra, &dc->client.base); + err = host1x_client_unregister(&dc->client); if (err < 0) { dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", err); diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index 1abcdbc7f4cd..c2db409bbd63 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c @@ -7,7 +7,8 @@ * published by the Free Software Foundation. */ -#include "host1x_client.h" +#include + #include "drm.h" #include "gem.h" @@ -22,239 +23,25 @@ struct tegra_drm_file { struct list_head contexts; }; -struct host1x_subdev { - struct host1x_client *client; - struct device_node *np; - struct list_head list; -}; - -static int host1x_subdev_add(struct tegra_drm *tegra, struct device_node *np) -{ - struct host1x_subdev *subdev; - - subdev = kzalloc(sizeof(*subdev), GFP_KERNEL); - if (!subdev) - return -ENOMEM; - - INIT_LIST_HEAD(&subdev->list); - subdev->np = of_node_get(np); - - list_add_tail(&subdev->list, &tegra->subdevs); - - return 0; -} - -static int host1x_subdev_register(struct tegra_drm *tegra, - struct host1x_subdev *subdev, - struct host1x_client *client) -{ - mutex_lock(&tegra->subdevs_lock); - list_del_init(&subdev->list); - list_add_tail(&subdev->list, &tegra->active); - subdev->client = client; - mutex_unlock(&tegra->subdevs_lock); - - return 0; -} - -static int host1x_subdev_unregister(struct tegra_drm *tegra, - struct host1x_subdev *subdev) -{ - mutex_lock(&tegra->subdevs_lock); - list_del_init(&subdev->list); - mutex_unlock(&tegra->subdevs_lock); - - of_node_put(subdev->np); - kfree(subdev); - - return 0; -} - -static int tegra_parse_dt(struct tegra_drm *tegra) -{ - static const char * const compat[] = { - "nvidia,tegra20-dc", - "nvidia,tegra20-hdmi", - "nvidia,tegra20-gr2d", - "nvidia,tegra30-dc", - "nvidia,tegra30-hdmi", - "nvidia,tegra30-gr2d", - }; - unsigned int i; - int err; - - for (i = 0; i < ARRAY_SIZE(compat); i++) { - struct device_node *np; - - for_each_child_of_node(tegra->dev->of_node, np) { - if (of_device_is_compatible(np, compat[i]) && - of_device_is_available(np)) { - err = host1x_subdev_add(tegra, np); - if (err < 0) - return err; - } - } - } - - return 0; -} - -int tegra_drm_alloc(struct platform_device *pdev) +static int tegra_drm_load(struct drm_device *drm, unsigned long flags) { + struct host1x_device *device = to_host1x_device(drm->dev); struct tegra_drm *tegra; int err; - tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); + tegra = kzalloc(sizeof(*tegra), GFP_KERNEL); if (!tegra) return -ENOMEM; - mutex_init(&tegra->subdevs_lock); - INIT_LIST_HEAD(&tegra->subdevs); - INIT_LIST_HEAD(&tegra->active); + dev_set_drvdata(drm->dev, tegra); mutex_init(&tegra->clients_lock); INIT_LIST_HEAD(&tegra->clients); - tegra->dev = &pdev->dev; - - err = tegra_parse_dt(tegra); - if (err < 0) { - dev_err(&pdev->dev, "failed to parse DT: %d\n", err); - return err; - } - - host1x_set_drm_data(&pdev->dev, tegra); - - return 0; -} - -int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm) -{ - struct host1x_client *client; - int err; - - mutex_lock(&tegra->clients_lock); - - list_for_each_entry(client, &tegra->clients, list) { - struct tegra_drm_client *tdc = to_tegra_drm_client(client); - - /* associate client with DRM device */ - tdc->drm = drm; - - if (client->ops && client->ops->init) { - err = client->ops->init(client); - if (err < 0) { - dev_err(tegra->dev, - "DRM setup failed for %s: %d\n", - dev_name(client->dev), err); - mutex_unlock(&tegra->clients_lock); - return err; - } - } - } - - mutex_unlock(&tegra->clients_lock); - - return 0; -} - -int tegra_drm_exit(struct tegra_drm *tegra) -{ - struct host1x_client *client; - struct platform_device *pdev; - int err; - - if (!tegra->drm) - return 0; - - mutex_lock(&tegra->clients_lock); - - list_for_each_entry_reverse(client, &tegra->clients, list) { - if (client->ops && client->ops->exit) { - err = client->ops->exit(client); - if (err < 0) { - dev_err(tegra->dev, - "DRM cleanup failed for %s: %d\n", - dev_name(client->dev), err); - mutex_unlock(&tegra->clients_lock); - return err; - } - } - } - - mutex_unlock(&tegra->clients_lock); - - pdev = to_platform_device(tegra->dev); - drm_platform_exit(&tegra_drm_driver, pdev); - tegra->drm = NULL; - - return 0; -} - -int host1x_register_client(struct tegra_drm *tegra, - struct host1x_client *client) -{ - struct host1x_subdev *subdev, *tmp; - int err; - - mutex_lock(&tegra->clients_lock); - list_add_tail(&client->list, &tegra->clients); - mutex_unlock(&tegra->clients_lock); - - list_for_each_entry_safe(subdev, tmp, &tegra->subdevs, list) - if (subdev->np == client->dev->of_node) - host1x_subdev_register(tegra, subdev, client); - - if (list_empty(&tegra->subdevs)) { - struct platform_device *pdev = to_platform_device(tegra->dev); - - err = drm_platform_init(&tegra_drm_driver, pdev); - if (err < 0) { - dev_err(tegra->dev, "drm_platform_init(): %d\n", err); - return err; - } - } - - return 0; -} - -int host1x_unregister_client(struct tegra_drm *tegra, - struct host1x_client *client) -{ - struct host1x_subdev *subdev, *tmp; - int err; - - list_for_each_entry_safe(subdev, tmp, &tegra->active, list) { - if (subdev->client == client) { - err = tegra_drm_exit(tegra); - if (err < 0) { - dev_err(tegra->dev, "tegra_drm_exit(): %d\n", - err); - return err; - } - - host1x_subdev_unregister(tegra, subdev); - break; - } - } - - mutex_lock(&tegra->clients_lock); - list_del_init(&client->list); - mutex_unlock(&tegra->clients_lock); - - return 0; -} - -static int tegra_drm_load(struct drm_device *drm, unsigned long flags) -{ - struct tegra_drm *tegra; - int err; - - tegra = host1x_get_drm_data(drm->dev); drm->dev_private = tegra; tegra->drm = drm; drm_mode_config_init(drm); - err = tegra_drm_init(tegra, drm); + err = host1x_device_init(device); if (err < 0) return err; @@ -280,9 +67,16 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) static int tegra_drm_unload(struct drm_device *drm) { + struct host1x_device *device = to_host1x_device(drm->dev); + int err; + drm_kms_helper_poll_fini(drm); tegra_drm_fb_exit(drm); + err = host1x_device_exit(device); + if (err < 0) + return err; + drm_mode_config_cleanup(drm); return 0; @@ -370,10 +164,11 @@ static int tegra_gem_mmap(struct drm_device *drm, void *data, static int tegra_syncpt_read(struct drm_device *drm, void *data, struct drm_file *file) { + struct host1x *host = dev_get_drvdata(drm->dev->parent); struct drm_tegra_syncpt_read *args = data; - struct host1x *host = dev_get_drvdata(drm->dev); - struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id); + struct host1x_syncpt *sp; + sp = host1x_syncpt_get(host, args->id); if (!sp) return -EINVAL; @@ -384,10 +179,11 @@ static int tegra_syncpt_read(struct drm_device *drm, void *data, static int tegra_syncpt_incr(struct drm_device *drm, void *data, struct drm_file *file) { + struct host1x *host1x = dev_get_drvdata(drm->dev->parent); struct drm_tegra_syncpt_incr *args = data; - struct host1x *host = dev_get_drvdata(drm->dev); - struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id); + struct host1x_syncpt *sp; + sp = host1x_syncpt_get(host1x, args->id); if (!sp) return -EINVAL; @@ -397,10 +193,11 @@ static int tegra_syncpt_incr(struct drm_device *drm, void *data, static int tegra_syncpt_wait(struct drm_device *drm, void *data, struct drm_file *file) { + struct host1x *host1x = dev_get_drvdata(drm->dev->parent); struct drm_tegra_syncpt_wait *args = data; - struct host1x *host = dev_get_drvdata(drm->dev); - struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id); + struct host1x_syncpt *sp; + sp = host1x_syncpt_get(host1x, args->id); if (!sp) return -EINVAL; @@ -422,7 +219,7 @@ static int tegra_open_channel(struct drm_device *drm, void *data, if (!context) return -ENOMEM; - list_for_each_entry(client, &tegra->clients, base.list) + list_for_each_entry(client, &tegra->clients, list) if (client->base.class == args->client) { err = client->ops->open_channel(client, context); if (err) @@ -441,8 +238,8 @@ static int tegra_open_channel(struct drm_device *drm, void *data, static int tegra_close_channel(struct drm_device *drm, void *data, struct drm_file *file) { - struct drm_tegra_close_channel *args = data; struct tegra_drm_file *fpriv = file->driver_priv; + struct drm_tegra_close_channel *args = data; struct tegra_drm_context *context; context = tegra_drm_get_context(args->context); @@ -652,3 +449,97 @@ struct drm_driver tegra_drm_driver = { .minor = DRIVER_MINOR, .patchlevel = DRIVER_PATCHLEVEL, }; + +int tegra_drm_register_client(struct tegra_drm *tegra, + struct tegra_drm_client *client) +{ + mutex_lock(&tegra->clients_lock); + list_add_tail(&client->list, &tegra->clients); + mutex_unlock(&tegra->clients_lock); + + return 0; +} + +int tegra_drm_unregister_client(struct tegra_drm *tegra, + struct tegra_drm_client *client) +{ + mutex_lock(&tegra->clients_lock); + list_del_init(&client->list); + mutex_unlock(&tegra->clients_lock); + + return 0; +} + +static int host1x_drm_probe(struct host1x_device *device) +{ + return drm_host1x_init(&tegra_drm_driver, device); +} + +static int host1x_drm_remove(struct host1x_device *device) +{ + drm_host1x_exit(&tegra_drm_driver, device); + + return 0; +} + +static const struct of_device_id host1x_drm_subdevs[] = { + { .compatible = "nvidia,tegra20-dc", }, + { .compatible = "nvidia,tegra20-hdmi", }, + { .compatible = "nvidia,tegra20-gr2d", }, + { .compatible = "nvidia,tegra30-dc", }, + { .compatible = "nvidia,tegra30-hdmi", }, + { .compatible = "nvidia,tegra30-gr2d", }, + { /* sentinel */ } +}; + +static struct host1x_driver host1x_drm_driver = { + .name = "drm", + .probe = host1x_drm_probe, + .remove = host1x_drm_remove, + .subdevs = host1x_drm_subdevs, +}; + +static int __init host1x_drm_init(void) +{ + int err; + + err = host1x_driver_register(&host1x_drm_driver); + if (err < 0) + return err; + + err = platform_driver_register(&tegra_dc_driver); + if (err < 0) + goto unregister_host1x; + + err = platform_driver_register(&tegra_hdmi_driver); + if (err < 0) + goto unregister_dc; + + err = platform_driver_register(&tegra_gr2d_driver); + if (err < 0) + goto unregister_hdmi; + + return 0; + +unregister_hdmi: + platform_driver_unregister(&tegra_hdmi_driver); +unregister_dc: + platform_driver_unregister(&tegra_dc_driver); +unregister_host1x: + host1x_driver_unregister(&host1x_drm_driver); + return err; +} +module_init(host1x_drm_init); + +static void __exit host1x_drm_exit(void) +{ + platform_driver_unregister(&tegra_gr2d_driver); + platform_driver_unregister(&tegra_hdmi_driver); + platform_driver_unregister(&tegra_dc_driver); + host1x_driver_unregister(&host1x_drm_driver); +} +module_exit(host1x_drm_exit); + +MODULE_AUTHOR("Thierry Reding "); +MODULE_DESCRIPTION("NVIDIA Tegra DRM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/host1x/drm/drm.h b/drivers/gpu/host1x/drm/drm.h index 8c26c6b1f5e1..25522e23c7b8 100644 --- a/drivers/gpu/host1x/drm/drm.h +++ b/drivers/gpu/host1x/drm/drm.h @@ -32,11 +32,6 @@ struct tegra_fbdev { struct tegra_drm { struct drm_device *drm; - struct device *dev; - - struct mutex subdevs_lock; - struct list_head subdevs; - struct list_head active; struct mutex clients_lock; struct list_head clients; @@ -63,29 +58,29 @@ struct tegra_drm_client_ops { struct tegra_drm_client { struct host1x_client base; - struct drm_device *drm; + struct list_head list; const struct tegra_drm_client_ops *ops; }; static inline struct tegra_drm_client * -to_tegra_drm_client(struct host1x_client *client) +host1x_to_drm_client(struct host1x_client *client) { return container_of(client, struct tegra_drm_client, base); } +extern int tegra_drm_register_client(struct tegra_drm *tegra, + struct tegra_drm_client *client); +extern int tegra_drm_unregister_client(struct tegra_drm *tegra, + struct tegra_drm_client *client); + extern int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm); extern int tegra_drm_exit(struct tegra_drm *tegra); -extern int host1x_register_client(struct tegra_drm *tegra, - struct host1x_client *client); -extern int host1x_unregister_client(struct tegra_drm *tegra, - struct host1x_client *client); - struct tegra_output; struct tegra_dc { - struct tegra_drm_client client; + struct host1x_client client; struct device *dev; spinlock_t lock; @@ -109,7 +104,7 @@ struct tegra_dc { }; static inline struct tegra_dc * -tegra_drm_client_to_dc(struct tegra_drm_client *client) +host1x_client_to_dc(struct host1x_client *client) { return container_of(client, struct tegra_dc, client); } @@ -235,6 +230,10 @@ static inline int tegra_output_check_mode(struct tegra_output *output, return output ? -ENOSYS : -EINVAL; } +/* from bus.c */ +int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device); +void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device); + /* from rgb.c */ extern int tegra_dc_rgb_probe(struct tegra_dc *dc); extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc); @@ -252,6 +251,8 @@ extern int tegra_drm_fb_init(struct drm_device *drm); extern void tegra_drm_fb_exit(struct drm_device *drm); extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); -extern struct drm_driver tegra_drm_driver; +extern struct platform_driver tegra_dc_driver; +extern struct platform_driver tegra_hdmi_driver; +extern struct platform_driver tegra_gr2d_driver; #endif /* HOST1X_DRM_H */ diff --git a/drivers/gpu/host1x/drm/gr2d.c b/drivers/gpu/host1x/drm/gr2d.c index dfb822428ca0..4e407e30da1c 100644 --- a/drivers/gpu/host1x/drm/gr2d.c +++ b/drivers/gpu/host1x/drm/gr2d.c @@ -16,7 +16,6 @@ #include -#include "host1x_client.h" #include "drm.h" #include "gem.h" @@ -35,19 +34,45 @@ static inline struct gr2d *to_gr2d(struct tegra_drm_client *client) return container_of(client, struct gr2d, client); } -static int gr2d_client_init(struct host1x_client *client) +static int gr2d_init(struct host1x_client *client) { - return 0; + struct tegra_drm_client *drm = host1x_to_drm_client(client); + struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct gr2d *gr2d = to_gr2d(drm); + + gr2d->channel = host1x_channel_request(client->dev); + if (!gr2d->channel) + return -ENOMEM; + + client->syncpts[0] = host1x_syncpt_request(client->dev, false); + if (!client->syncpts[0]) { + host1x_channel_free(gr2d->channel); + return -ENOMEM; + } + + return tegra_drm_register_client(tegra, drm); } -static int gr2d_client_exit(struct host1x_client *client) +static int gr2d_exit(struct host1x_client *client) { + struct tegra_drm_client *drm = host1x_to_drm_client(client); + struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct gr2d *gr2d = to_gr2d(drm); + int err; + + err = tegra_drm_unregister_client(tegra, drm); + if (err < 0) + return err; + + host1x_syncpt_free(client->syncpts[0]); + host1x_channel_free(gr2d->channel); + return 0; } static const struct host1x_client_ops gr2d_client_ops = { - .init = gr2d_client_init, - .exit = gr2d_client_exit, + .init = gr2d_init, + .exit = gr2d_exit, }; static int gr2d_open_channel(struct tegra_drm_client *client, @@ -240,7 +265,6 @@ static const u32 gr2d_addr_regs[] = { static int gr2d_probe(struct platform_device *pdev) { - struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent); struct device *dev = &pdev->dev; struct host1x_syncpt **syncpts; struct gr2d *gr2d; @@ -267,25 +291,17 @@ static int gr2d_probe(struct platform_device *pdev) return err; } - gr2d->channel = host1x_channel_request(dev); - if (!gr2d->channel) - return -ENOMEM; - - *syncpts = host1x_syncpt_request(dev, false); - if (!(*syncpts)) { - host1x_channel_free(gr2d->channel); - return -ENOMEM; - } - INIT_LIST_HEAD(&gr2d->client.base.list); gr2d->client.base.ops = &gr2d_client_ops; gr2d->client.base.dev = dev; gr2d->client.base.class = HOST1X_CLASS_GR2D; gr2d->client.base.syncpts = syncpts; gr2d->client.base.num_syncpts = 1; + + INIT_LIST_HEAD(&gr2d->client.list); gr2d->client.ops = &gr2d_ops; - err = host1x_register_client(tegra, &gr2d->client.base); + err = host1x_client_register(&gr2d->client.base); if (err < 0) { dev_err(dev, "failed to register host1x client: %d\n", err); return err; @@ -302,22 +318,16 @@ static int gr2d_probe(struct platform_device *pdev) static int gr2d_remove(struct platform_device *pdev) { - struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent); struct gr2d *gr2d = platform_get_drvdata(pdev); - unsigned int i; int err; - err = host1x_unregister_client(tegra, &gr2d->client.base); + err = host1x_client_unregister(&gr2d->client.base); if (err < 0) { dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", err); return err; } - for (i = 0; i < gr2d->client.base.num_syncpts; i++) - host1x_syncpt_free(gr2d->client.base.syncpts[i]); - - host1x_channel_free(gr2d->channel); clk_disable_unprepare(gr2d->clk); return 0; diff --git a/drivers/gpu/host1x/drm/hdmi.c b/drivers/gpu/host1x/drm/hdmi.c index a2370045993c..f5663d15670b 100644 --- a/drivers/gpu/host1x/drm/hdmi.c +++ b/drivers/gpu/host1x/drm/hdmi.c @@ -13,13 +13,12 @@ #include #include -#include "host1x_client.h" #include "hdmi.h" #include "drm.h" #include "dc.h" struct tegra_hdmi { - struct tegra_drm_client client; + struct host1x_client client; struct tegra_output output; struct device *dev; @@ -43,7 +42,7 @@ struct tegra_hdmi { }; static inline struct tegra_hdmi * -tegra_drm_client_to_hdmi(struct tegra_drm_client *client) +host1x_client_to_hdmi(struct host1x_client *client) { return container_of(client, struct tegra_hdmi, client); } @@ -1118,22 +1117,22 @@ static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi) static int tegra_hdmi_init(struct host1x_client *client) { - struct tegra_drm_client *drm = to_tegra_drm_client(client); - struct tegra_hdmi *hdmi = tegra_drm_client_to_hdmi(drm); + struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); int err; hdmi->output.type = TEGRA_OUTPUT_HDMI; hdmi->output.dev = client->dev; hdmi->output.ops = &hdmi_ops; - err = tegra_output_init(drm->drm, &hdmi->output); + err = tegra_output_init(tegra->drm, &hdmi->output); if (err < 0) { dev_err(client->dev, "output setup failed: %d\n", err); return err; } if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_hdmi_debugfs_init(hdmi, drm->drm->primary); + err = tegra_hdmi_debugfs_init(hdmi, tegra->drm->primary); if (err < 0) dev_err(client->dev, "debugfs setup failed: %d\n", err); } @@ -1143,8 +1142,7 @@ static int tegra_hdmi_init(struct host1x_client *client) static int tegra_hdmi_exit(struct host1x_client *client) { - struct tegra_drm_client *drm = to_tegra_drm_client(client); - struct tegra_hdmi *hdmi = tegra_drm_client_to_hdmi(drm); + struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); int err; if (IS_ENABLED(CONFIG_DEBUG_FS)) { @@ -1176,7 +1174,6 @@ static const struct host1x_client_ops hdmi_client_ops = { static int tegra_hdmi_probe(struct platform_device *pdev) { - struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent); struct tegra_hdmi *hdmi; struct resource *regs; int err; @@ -1247,11 +1244,11 @@ static int tegra_hdmi_probe(struct platform_device *pdev) hdmi->irq = err; - INIT_LIST_HEAD(&hdmi->client.base.list); - hdmi->client.base.ops = &hdmi_client_ops; - hdmi->client.base.dev = &pdev->dev; + INIT_LIST_HEAD(&hdmi->client.list); + hdmi->client.ops = &hdmi_client_ops; + hdmi->client.dev = &pdev->dev; - err = host1x_register_client(tegra, &hdmi->client.base); + err = host1x_client_register(&hdmi->client); if (err < 0) { dev_err(&pdev->dev, "failed to register host1x client: %d\n", err); @@ -1265,11 +1262,10 @@ static int tegra_hdmi_probe(struct platform_device *pdev) static int tegra_hdmi_remove(struct platform_device *pdev) { - struct tegra_drm *tegra = host1x_get_drm_data(pdev->dev.parent); struct tegra_hdmi *hdmi = platform_get_drvdata(pdev); int err; - err = host1x_unregister_client(tegra, &hdmi->client.base); + err = host1x_client_unregister(&hdmi->client); if (err < 0) { dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", err); diff --git a/drivers/gpu/host1x/host1x_client.h b/drivers/gpu/host1x/host1x_client.h deleted file mode 100644 index 6a0bd0268042..000000000000 --- a/drivers/gpu/host1x/host1x_client.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2013, NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef HOST1X_CLIENT_H -#define HOST1X_CLIENT_H - -struct device; -struct platform_device; - -#ifdef CONFIG_DRM_TEGRA -int tegra_drm_alloc(struct platform_device *pdev); -#else -static inline int tegra_drm_alloc(struct platform_device *pdev) -{ - return 0; -} -#endif - -void host1x_set_drm_data(struct device *dev, void *data); -void *host1x_get_drm_data(struct device *dev); - -#endif diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 2b954adf5bd4..ffd8ad92cdf9 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -150,6 +150,7 @@ int drm_err(const char *func, const char *format, ...); #define DRIVER_BUS_PCI 0x1 #define DRIVER_BUS_PLATFORM 0x2 #define DRIVER_BUS_USB 0x3 +#define DRIVER_BUS_HOST1X 0x4 /***********************************************************************/ /** \name Begin the DRM... */ diff --git a/include/linux/host1x.h b/include/linux/host1x.h index 7442f2a57039..e62c61a4afa9 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -19,7 +19,7 @@ #ifndef __LINUX_HOST1X_H #define __LINUX_HOST1X_H -#include +#include #include enum host1x_class { @@ -37,6 +37,7 @@ struct host1x_client_ops { struct host1x_client { struct list_head list; + struct device *parent; struct device *dev; const struct host1x_client_ops *ops; @@ -230,4 +231,46 @@ void host1x_job_put(struct host1x_job *job); int host1x_job_pin(struct host1x_job *job, struct device *dev); void host1x_job_unpin(struct host1x_job *job); +/* + * subdevice probe infrastructure + */ + +struct host1x_device; + +struct host1x_driver { + const struct of_device_id *subdevs; + struct list_head list; + const char *name; + + int (*probe)(struct host1x_device *device); + int (*remove)(struct host1x_device *device); +}; + +int host1x_driver_register(struct host1x_driver *driver); +void host1x_driver_unregister(struct host1x_driver *driver); + +struct host1x_device { + struct host1x_driver *driver; + struct list_head list; + struct device dev; + + struct mutex subdevs_lock; + struct list_head subdevs; + struct list_head active; + + struct mutex clients_lock; + struct list_head clients; +}; + +static inline struct host1x_device *to_host1x_device(struct device *dev) +{ + return container_of(dev, struct host1x_device, dev); +} + +int host1x_device_init(struct host1x_device *device); +int host1x_device_exit(struct host1x_device *device); + +int host1x_client_register(struct host1x_client *client); +int host1x_client_unregister(struct host1x_client *client); + #endif -- cgit v1.2.3 From 5f60ed0d840d53e9d65aa54e1a5365af8ce2769e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 28 Feb 2013 08:08:01 +0100 Subject: drm/tegra: Add 3D support Initialize and power the 3D unit on Tegra20, Tegra30 and Tegra114 and register a channel with the Tegra DRM driver so that the unit can be used from userspace. Signed-off-by: Thierry Reding Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/Makefile | 3 +- drivers/gpu/drm/tegra/drm.c | 10 ++ drivers/gpu/drm/tegra/drm.h | 1 + drivers/gpu/drm/tegra/gr3d.c | 337 +++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/gr3d.h | 27 ++++ include/linux/host1x.h | 1 + 6 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/tegra/gr3d.c create mode 100644 drivers/gpu/drm/tegra/gr3d.h (limited to 'include') diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile index e1cdf1da2778..edc76abd58bb 100644 --- a/drivers/gpu/drm/tegra/Makefile +++ b/drivers/gpu/drm/tegra/Makefile @@ -9,6 +9,7 @@ tegra-drm-y := \ output.o \ rgb.o \ hdmi.o \ - gr2d.o + gr2d.o \ + gr3d.o obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 6f42f1692f29..26a2903879d8 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -615,10 +615,13 @@ static const struct of_device_id host1x_drm_subdevs[] = { { .compatible = "nvidia,tegra20-dc", }, { .compatible = "nvidia,tegra20-hdmi", }, { .compatible = "nvidia,tegra20-gr2d", }, + { .compatible = "nvidia,tegra20-gr3d", }, { .compatible = "nvidia,tegra30-dc", }, { .compatible = "nvidia,tegra30-hdmi", }, { .compatible = "nvidia,tegra30-gr2d", }, + { .compatible = "nvidia,tegra30-gr3d", }, { .compatible = "nvidia,tegra114-hdmi", }, + { .compatible = "nvidia,tegra114-gr3d", }, { /* sentinel */ } }; @@ -649,8 +652,14 @@ static int __init host1x_drm_init(void) if (err < 0) goto unregister_hdmi; + err = platform_driver_register(&tegra_gr3d_driver); + if (err < 0) + goto unregister_gr2d; + return 0; +unregister_gr2d: + platform_driver_unregister(&tegra_gr2d_driver); unregister_hdmi: platform_driver_unregister(&tegra_hdmi_driver); unregister_dc: @@ -663,6 +672,7 @@ module_init(host1x_drm_init); static void __exit host1x_drm_exit(void) { + platform_driver_unregister(&tegra_gr3d_driver); platform_driver_unregister(&tegra_gr2d_driver); platform_driver_unregister(&tegra_hdmi_driver); platform_driver_unregister(&tegra_dc_driver); diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index e3cd3ed438d5..b599fd4650ad 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -261,5 +261,6 @@ extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); extern struct platform_driver tegra_dc_driver; extern struct platform_driver tegra_hdmi_driver; extern struct platform_driver tegra_gr2d_driver; +extern struct platform_driver tegra_gr3d_driver; #endif /* HOST1X_DRM_H */ diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c new file mode 100644 index 000000000000..dc0f222d1323 --- /dev/null +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2013 Avionic Design GmbH + * Copyright (C) 2013 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include "drm.h" +#include "gem.h" +#include "gr3d.h" + +struct gr3d { + struct tegra_drm_client client; + struct host1x_channel *channel; + struct clk *clk_secondary; + struct clk *clk; + + DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS); +}; + +static inline struct gr3d *to_gr3d(struct tegra_drm_client *client) +{ + return container_of(client, struct gr3d, client); +} + +static int gr3d_init(struct host1x_client *client) +{ + struct tegra_drm_client *drm = host1x_to_drm_client(client); + struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct gr3d *gr3d = to_gr3d(drm); + + gr3d->channel = host1x_channel_request(client->dev); + if (!gr3d->channel) + return -ENOMEM; + + client->syncpts[0] = host1x_syncpt_request(client->dev, 0); + if (!client->syncpts[0]) { + host1x_channel_free(gr3d->channel); + return -ENOMEM; + } + + return tegra_drm_register_client(tegra, drm); +} + +static int gr3d_exit(struct host1x_client *client) +{ + struct tegra_drm_client *drm = host1x_to_drm_client(client); + struct tegra_drm *tegra = dev_get_drvdata(client->parent); + struct gr3d *gr3d = to_gr3d(drm); + int err; + + err = tegra_drm_unregister_client(tegra, drm); + if (err < 0) + return err; + + host1x_syncpt_free(client->syncpts[0]); + host1x_channel_free(gr3d->channel); + + return 0; +} + +static const struct host1x_client_ops gr3d_client_ops = { + .init = gr3d_init, + .exit = gr3d_exit, +}; + +static int gr3d_open_channel(struct tegra_drm_client *client, + struct tegra_drm_context *context) +{ + struct gr3d *gr3d = to_gr3d(client); + + context->channel = host1x_channel_get(gr3d->channel); + if (!context->channel) + return -ENOMEM; + + return 0; +} + +static void gr3d_close_channel(struct tegra_drm_context *context) +{ + host1x_channel_put(context->channel); +} + +static int gr3d_is_addr_reg(struct device *dev, u32 class, u32 offset) +{ + struct gr3d *gr3d = dev_get_drvdata(dev); + + switch (class) { + case HOST1X_CLASS_HOST1X: + if (offset == 0x2b) + return 1; + + break; + + case HOST1X_CLASS_GR3D: + if (offset >= GR3D_NUM_REGS) + break; + + if (test_bit(offset, gr3d->addr_regs)) + return 1; + + break; + } + + return 0; +} + +static const struct tegra_drm_client_ops gr3d_ops = { + .open_channel = gr3d_open_channel, + .close_channel = gr3d_close_channel, + .is_addr_reg = gr3d_is_addr_reg, + .submit = tegra_drm_submit, +}; + +static const struct of_device_id tegra_gr3d_match[] = { + { .compatible = "nvidia,tegra114-gr3d" }, + { .compatible = "nvidia,tegra30-gr3d" }, + { .compatible = "nvidia,tegra20-gr3d" }, + { } +}; + +static const u32 gr3d_addr_regs[] = { + GR3D_IDX_ATTRIBUTE( 0), + GR3D_IDX_ATTRIBUTE( 1), + GR3D_IDX_ATTRIBUTE( 2), + GR3D_IDX_ATTRIBUTE( 3), + GR3D_IDX_ATTRIBUTE( 4), + GR3D_IDX_ATTRIBUTE( 5), + GR3D_IDX_ATTRIBUTE( 6), + GR3D_IDX_ATTRIBUTE( 7), + GR3D_IDX_ATTRIBUTE( 8), + GR3D_IDX_ATTRIBUTE( 9), + GR3D_IDX_ATTRIBUTE(10), + GR3D_IDX_ATTRIBUTE(11), + GR3D_IDX_ATTRIBUTE(12), + GR3D_IDX_ATTRIBUTE(13), + GR3D_IDX_ATTRIBUTE(14), + GR3D_IDX_ATTRIBUTE(15), + GR3D_IDX_INDEX_BASE, + GR3D_QR_ZTAG_ADDR, + GR3D_QR_CTAG_ADDR, + GR3D_QR_CZ_ADDR, + GR3D_TEX_TEX_ADDR( 0), + GR3D_TEX_TEX_ADDR( 1), + GR3D_TEX_TEX_ADDR( 2), + GR3D_TEX_TEX_ADDR( 3), + GR3D_TEX_TEX_ADDR( 4), + GR3D_TEX_TEX_ADDR( 5), + GR3D_TEX_TEX_ADDR( 6), + GR3D_TEX_TEX_ADDR( 7), + GR3D_TEX_TEX_ADDR( 8), + GR3D_TEX_TEX_ADDR( 9), + GR3D_TEX_TEX_ADDR(10), + GR3D_TEX_TEX_ADDR(11), + GR3D_TEX_TEX_ADDR(12), + GR3D_TEX_TEX_ADDR(13), + GR3D_TEX_TEX_ADDR(14), + GR3D_TEX_TEX_ADDR(15), + GR3D_DW_MEMORY_OUTPUT_ADDRESS, + GR3D_GLOBAL_SURFADDR( 0), + GR3D_GLOBAL_SURFADDR( 1), + GR3D_GLOBAL_SURFADDR( 2), + GR3D_GLOBAL_SURFADDR( 3), + GR3D_GLOBAL_SURFADDR( 4), + GR3D_GLOBAL_SURFADDR( 5), + GR3D_GLOBAL_SURFADDR( 6), + GR3D_GLOBAL_SURFADDR( 7), + GR3D_GLOBAL_SURFADDR( 8), + GR3D_GLOBAL_SURFADDR( 9), + GR3D_GLOBAL_SURFADDR(10), + GR3D_GLOBAL_SURFADDR(11), + GR3D_GLOBAL_SURFADDR(12), + GR3D_GLOBAL_SURFADDR(13), + GR3D_GLOBAL_SURFADDR(14), + GR3D_GLOBAL_SURFADDR(15), + GR3D_GLOBAL_SPILLSURFADDR, + GR3D_GLOBAL_SURFOVERADDR( 0), + GR3D_GLOBAL_SURFOVERADDR( 1), + GR3D_GLOBAL_SURFOVERADDR( 2), + GR3D_GLOBAL_SURFOVERADDR( 3), + GR3D_GLOBAL_SURFOVERADDR( 4), + GR3D_GLOBAL_SURFOVERADDR( 5), + GR3D_GLOBAL_SURFOVERADDR( 6), + GR3D_GLOBAL_SURFOVERADDR( 7), + GR3D_GLOBAL_SURFOVERADDR( 8), + GR3D_GLOBAL_SURFOVERADDR( 9), + GR3D_GLOBAL_SURFOVERADDR(10), + GR3D_GLOBAL_SURFOVERADDR(11), + GR3D_GLOBAL_SURFOVERADDR(12), + GR3D_GLOBAL_SURFOVERADDR(13), + GR3D_GLOBAL_SURFOVERADDR(14), + GR3D_GLOBAL_SURFOVERADDR(15), + GR3D_GLOBAL_SAMP01SURFADDR( 0), + GR3D_GLOBAL_SAMP01SURFADDR( 1), + GR3D_GLOBAL_SAMP01SURFADDR( 2), + GR3D_GLOBAL_SAMP01SURFADDR( 3), + GR3D_GLOBAL_SAMP01SURFADDR( 4), + GR3D_GLOBAL_SAMP01SURFADDR( 5), + GR3D_GLOBAL_SAMP01SURFADDR( 6), + GR3D_GLOBAL_SAMP01SURFADDR( 7), + GR3D_GLOBAL_SAMP01SURFADDR( 8), + GR3D_GLOBAL_SAMP01SURFADDR( 9), + GR3D_GLOBAL_SAMP01SURFADDR(10), + GR3D_GLOBAL_SAMP01SURFADDR(11), + GR3D_GLOBAL_SAMP01SURFADDR(12), + GR3D_GLOBAL_SAMP01SURFADDR(13), + GR3D_GLOBAL_SAMP01SURFADDR(14), + GR3D_GLOBAL_SAMP01SURFADDR(15), + GR3D_GLOBAL_SAMP23SURFADDR( 0), + GR3D_GLOBAL_SAMP23SURFADDR( 1), + GR3D_GLOBAL_SAMP23SURFADDR( 2), + GR3D_GLOBAL_SAMP23SURFADDR( 3), + GR3D_GLOBAL_SAMP23SURFADDR( 4), + GR3D_GLOBAL_SAMP23SURFADDR( 5), + GR3D_GLOBAL_SAMP23SURFADDR( 6), + GR3D_GLOBAL_SAMP23SURFADDR( 7), + GR3D_GLOBAL_SAMP23SURFADDR( 8), + GR3D_GLOBAL_SAMP23SURFADDR( 9), + GR3D_GLOBAL_SAMP23SURFADDR(10), + GR3D_GLOBAL_SAMP23SURFADDR(11), + GR3D_GLOBAL_SAMP23SURFADDR(12), + GR3D_GLOBAL_SAMP23SURFADDR(13), + GR3D_GLOBAL_SAMP23SURFADDR(14), + GR3D_GLOBAL_SAMP23SURFADDR(15), +}; + +static int gr3d_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct host1x_syncpt **syncpts; + struct gr3d *gr3d; + unsigned int i; + int err; + + gr3d = devm_kzalloc(&pdev->dev, sizeof(*gr3d), GFP_KERNEL); + if (!gr3d) + return -ENOMEM; + + syncpts = devm_kzalloc(&pdev->dev, sizeof(*syncpts), GFP_KERNEL); + if (!syncpts) + return -ENOMEM; + + gr3d->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(gr3d->clk)) { + dev_err(&pdev->dev, "cannot get clock\n"); + return PTR_ERR(gr3d->clk); + } + + if (of_device_is_compatible(np, "nvidia,tegra30-gr3d")) { + gr3d->clk_secondary = devm_clk_get(&pdev->dev, "3d2"); + if (IS_ERR(gr3d->clk)) { + dev_err(&pdev->dev, "cannot get secondary clock\n"); + return PTR_ERR(gr3d->clk); + } + } + + err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D, gr3d->clk); + if (err < 0) { + dev_err(&pdev->dev, "failed to power up 3D unit\n"); + return err; + } + + if (gr3d->clk_secondary) { + err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D1, + gr3d->clk_secondary); + if (err < 0) { + dev_err(&pdev->dev, + "failed to power up secondary 3D unit\n"); + return err; + } + } + + INIT_LIST_HEAD(&gr3d->client.base.list); + gr3d->client.base.ops = &gr3d_client_ops; + gr3d->client.base.dev = &pdev->dev; + gr3d->client.base.class = HOST1X_CLASS_GR3D; + gr3d->client.base.syncpts = syncpts; + gr3d->client.base.num_syncpts = 1; + + INIT_LIST_HEAD(&gr3d->client.list); + gr3d->client.ops = &gr3d_ops; + + err = host1x_client_register(&gr3d->client.base); + if (err < 0) { + dev_err(&pdev->dev, "failed to register host1x client: %d\n", + err); + return err; + } + + /* initialize address register map */ + for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++) + set_bit(gr3d_addr_regs[i], gr3d->addr_regs); + + platform_set_drvdata(pdev, gr3d); + + return 0; +} + +static int gr3d_remove(struct platform_device *pdev) +{ + struct gr3d *gr3d = platform_get_drvdata(pdev); + int err; + + err = host1x_client_unregister(&gr3d->client.base); + if (err < 0) { + dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", + err); + return err; + } + + if (gr3d->clk_secondary) { + tegra_powergate_power_off(TEGRA_POWERGATE_3D1); + clk_disable_unprepare(gr3d->clk_secondary); + } + + tegra_powergate_power_off(TEGRA_POWERGATE_3D); + clk_disable_unprepare(gr3d->clk); + + return 0; +} + +struct platform_driver tegra_gr3d_driver = { + .driver = { + .name = "tegra-gr3d", + .of_match_table = tegra_gr3d_match, + }, + .probe = gr3d_probe, + .remove = gr3d_remove, +}; diff --git a/drivers/gpu/drm/tegra/gr3d.h b/drivers/gpu/drm/tegra/gr3d.h new file mode 100644 index 000000000000..0c30a1351c83 --- /dev/null +++ b/drivers/gpu/drm/tegra/gr3d.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef TEGRA_GR3D_H +#define TEGRA_GR3D_H + +#define GR3D_IDX_ATTRIBUTE(x) (0x100 + (x) * 2) +#define GR3D_IDX_INDEX_BASE 0x121 +#define GR3D_QR_ZTAG_ADDR 0x415 +#define GR3D_QR_CTAG_ADDR 0x417 +#define GR3D_QR_CZ_ADDR 0x419 +#define GR3D_TEX_TEX_ADDR(x) (0x710 + (x)) +#define GR3D_DW_MEMORY_OUTPUT_ADDRESS 0x904 +#define GR3D_GLOBAL_SURFADDR(x) (0xe00 + (x)) +#define GR3D_GLOBAL_SPILLSURFADDR 0xe2a +#define GR3D_GLOBAL_SURFOVERADDR(x) (0xe30 + (x)) +#define GR3D_GLOBAL_SAMP01SURFADDR(x) (0xe50 + (x)) +#define GR3D_GLOBAL_SAMP23SURFADDR(x) (0xe60 + (x)) + +#define GR3D_NUM_REGS 0xe88 + +#endif diff --git a/include/linux/host1x.h b/include/linux/host1x.h index e62c61a4afa9..f5dd56fbdc3e 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -26,6 +26,7 @@ enum host1x_class { HOST1X_CLASS_HOST1X = 0x1, HOST1X_CLASS_GR2D = 0x51, HOST1X_CLASS_GR2D_SB = 0x52, + HOST1X_CLASS_GR3D = 0x60, }; struct host1x_client; -- cgit v1.2.3 From 773af77fc479fd454c3f6836f86bf63996545cf4 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 4 Oct 2013 22:34:01 +0200 Subject: drm/tegra: Add support for tiled buffer objects The gr2d and gr3d engines work more efficiently on buffers with a tiled memory layout. Allow created buffers to be marked as tiled so that the display controller can scan them out properly. Signed-off-by: Thierry Reding Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 21 +++++++++++++++++++++ drivers/gpu/drm/tegra/dc.h | 4 ++++ drivers/gpu/drm/tegra/drm.c | 2 +- drivers/gpu/drm/tegra/drm.h | 2 ++ drivers/gpu/drm/tegra/fb.c | 12 +++++++++++- drivers/gpu/drm/tegra/gem.c | 13 ++++++++++--- drivers/gpu/drm/tegra/gem.h | 13 +++++++++---- include/uapi/drm/tegra_drm.h | 2 ++ 8 files changed, 60 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 054ca1b6bd31..c51aaf7555f5 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -47,6 +47,7 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, window.dst.h = crtc_h; window.format = tegra_dc_format(fb->pixel_format); window.bits_per_pixel = fb->bits_per_pixel; + window.tiled = tegra_fb_is_tiled(fb); for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) { struct tegra_bo *bo = tegra_fb_get_plane(fb, i); @@ -157,6 +158,16 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE); tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH); + if (tegra_fb_is_tiled(fb)) { + value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV | + DC_WIN_BUFFER_ADDR_MODE_TILE; + } else { + value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV | + DC_WIN_BUFFER_ADDR_MODE_LINEAR; + } + + tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE); + value = GENERAL_UPDATE | WIN_A_UPDATE; tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); @@ -509,6 +520,16 @@ int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); + if (window->tiled) { + value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV | + DC_WIN_BUFFER_ADDR_MODE_TILE; + } else { + value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV | + DC_WIN_BUFFER_ADDR_MODE_LINEAR; + } + + tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE); + value = WIN_ENABLE; if (yuv) { diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 79eaec9aac77..e0b94c26bb86 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -365,6 +365,10 @@ #define DC_WIN_BUF_STRIDE 0x70b #define DC_WIN_UV_BUF_STRIDE 0x70c #define DC_WIN_BUFFER_ADDR_MODE 0x70d +#define DC_WIN_BUFFER_ADDR_MODE_LINEAR (0 << 0) +#define DC_WIN_BUFFER_ADDR_MODE_TILE (1 << 0) +#define DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV (0 << 16) +#define DC_WIN_BUFFER_ADDR_MODE_TILE_UV (1 << 16) #define DC_WIN_DV_CONTROL 0x70e #define DC_WIN_BLEND_NOKEY 0x70f diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 26a2903879d8..ecb6ec735ef1 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -262,7 +262,7 @@ static int tegra_gem_create(struct drm_device *drm, void *data, struct drm_tegra_gem_create *args = data; struct tegra_bo *bo; - bo = tegra_bo_create_with_handle(file, drm, args->size, + bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags, &args->handle); if (IS_ERR(bo)) return PTR_ERR(bo); diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index b599fd4650ad..e07a10eedae6 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -148,6 +148,7 @@ struct tegra_dc_window { unsigned int format; unsigned int stride[2]; unsigned long base[3]; + bool tiled; }; /* from dc.c */ @@ -254,6 +255,7 @@ extern int tegra_output_exit(struct tegra_output *output); /* from fb.c */ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, unsigned int index); +bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer); extern int tegra_drm_fb_init(struct drm_device *drm); extern void tegra_drm_fb_exit(struct drm_device *drm); extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 1fd4e196b934..fdb00e040046 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -34,6 +34,16 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, return fb->planes[index]; } +bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer) +{ + struct tegra_fb *fb = to_tegra_fb(framebuffer); + + if (fb->planes[0]->flags & TEGRA_BO_TILED) + return true; + + return false; +} + static void tegra_fb_destroy(struct drm_framebuffer *framebuffer) { struct tegra_fb *fb = to_tegra_fb(framebuffer); @@ -188,7 +198,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, size = cmd.pitches[0] * cmd.height; - bo = tegra_bo_create(drm, size); + bo = tegra_bo_create(drm, size, 0); if (IS_ERR(bo)) return PTR_ERR(bo); diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 267c0c21e5cc..d851ec106cf4 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -18,6 +18,8 @@ * GNU General Public License for more details. */ +#include + #include "gem.h" static inline struct tegra_bo *host1x_to_tegra_bo(struct host1x_bo *bo) @@ -97,7 +99,8 @@ static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo) dma_free_writecombine(drm->dev, bo->gem.size, bo->vaddr, bo->paddr); } -struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size) +struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size, + unsigned long flags) { struct tegra_bo *bo; int err; @@ -126,6 +129,9 @@ struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size) if (err) goto err_mmap; + if (flags & DRM_TEGRA_GEM_CREATE_TILED) + bo->flags |= TEGRA_BO_TILED; + return bo; err_mmap: @@ -142,12 +148,13 @@ err_dma: struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file, struct drm_device *drm, unsigned int size, + unsigned long flags, unsigned int *handle) { struct tegra_bo *bo; int ret; - bo = tegra_bo_create(drm, size); + bo = tegra_bo_create(drm, size, flags); if (IS_ERR(bo)) return bo; @@ -187,7 +194,7 @@ int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, if (args->size < args->pitch * args->height) args->size = args->pitch * args->height; - bo = tegra_bo_create_with_handle(file, drm, args->size, + bo = tegra_bo_create_with_handle(file, drm, args->size, 0, &args->handle); if (IS_ERR(bo)) return PTR_ERR(bo); diff --git a/drivers/gpu/drm/tegra/gem.h b/drivers/gpu/drm/tegra/gem.h index 2b54f1425d5e..c4993fc2c3bd 100644 --- a/drivers/gpu/drm/tegra/gem.h +++ b/drivers/gpu/drm/tegra/gem.h @@ -24,9 +24,12 @@ #include #include +#define TEGRA_BO_TILED (1 << 0) + struct tegra_bo { struct drm_gem_object gem; struct host1x_bo base; + unsigned long flags; dma_addr_t paddr; void *vaddr; }; @@ -38,11 +41,13 @@ static inline struct tegra_bo *to_tegra_bo(struct drm_gem_object *gem) extern const struct host1x_bo_ops tegra_bo_ops; -struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size); +struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size, + unsigned long flags); struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file, - struct drm_device *drm, - unsigned int size, - unsigned int *handle); + struct drm_device *drm, + unsigned int size, + unsigned long flags, + unsigned int *handle); void tegra_bo_free_object(struct drm_gem_object *gem); int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, struct drm_mode_create_dumb *args); diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h index 73bde4eaf16c..6b420029a645 100644 --- a/include/uapi/drm/tegra_drm.h +++ b/include/uapi/drm/tegra_drm.h @@ -19,6 +19,8 @@ #include +#define DRM_TEGRA_GEM_CREATE_TILED (1 << 0) + struct drm_tegra_gem_create { __u64 size; __u32 flags; -- cgit v1.2.3 From db7fbdfd25ee009165b6c3b80a9d1c6d8534ad94 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 7 Oct 2013 09:47:58 +0200 Subject: drm/tegra: Support bottom-up buffer objects The gr3d engine renders images bottom-up. Allow buffers that are used for 3D content to be marked as such and implement support in the display controller to present them properly. Signed-off-by: Thierry Reding Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 24 ++++++++++++++++++++++++ drivers/gpu/drm/tegra/dc.h | 1 + drivers/gpu/drm/tegra/drm.h | 2 ++ drivers/gpu/drm/tegra/fb.c | 10 ++++++++++ drivers/gpu/drm/tegra/gem.c | 3 +++ drivers/gpu/drm/tegra/gem.h | 3 ++- include/uapi/drm/tegra_drm.h | 3 ++- 7 files changed, 44 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index c51aaf7555f5..ae1cb31ead7e 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -47,6 +47,7 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, window.dst.h = crtc_h; window.format = tegra_dc_format(fb->pixel_format); window.bits_per_pixel = fb->bits_per_pixel; + window.bottom_up = tegra_fb_is_bottom_up(fb); window.tiled = tegra_fb_is_tiled(fb); for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) { @@ -147,6 +148,7 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, { unsigned int format = tegra_dc_format(fb->pixel_format); struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); + unsigned int h_offset = 0, v_offset = 0; unsigned long value; tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); @@ -168,6 +170,22 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE); + /* make sure bottom-up buffers are properly displayed */ + if (tegra_fb_is_bottom_up(fb)) { + value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); + value |= INVERT_V; + tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); + + v_offset += fb->height - 1; + } else { + value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); + value &= ~INVERT_V; + tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); + } + + tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); + tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); + value = GENERAL_UPDATE | WIN_A_UPDATE; tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); @@ -517,6 +535,9 @@ int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE); } + if (window->bottom_up) + v_offset += window->src.h - 1; + tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); @@ -548,6 +569,9 @@ int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, value |= COLOR_EXPAND; } + if (window->bottom_up) + value |= INVERT_V; + tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); /* diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index e0b94c26bb86..91bbda291470 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -302,6 +302,7 @@ #define DC_WIN_CSC_KVB 0x618 #define DC_WIN_WIN_OPTIONS 0x700 +#define INVERT_V (1 << 2) #define COLOR_EXPAND (1 << 6) #define CSC_ENABLE (1 << 18) #define WIN_ENABLE (1 << 30) diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index e07a10eedae6..fdfe259ed7f8 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -148,6 +148,7 @@ struct tegra_dc_window { unsigned int format; unsigned int stride[2]; unsigned long base[3]; + bool bottom_up; bool tiled; }; @@ -255,6 +256,7 @@ extern int tegra_output_exit(struct tegra_output *output); /* from fb.c */ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, unsigned int index); +bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer); bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer); extern int tegra_drm_fb_init(struct drm_device *drm); extern void tegra_drm_fb_exit(struct drm_device *drm); diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index fdb00e040046..490f7719e317 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -34,6 +34,16 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, return fb->planes[index]; } +bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer) +{ + struct tegra_fb *fb = to_tegra_fb(framebuffer); + + if (fb->planes[0]->flags & TEGRA_BO_BOTTOM_UP) + return true; + + return false; +} + bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer) { struct tegra_fb *fb = to_tegra_fb(framebuffer); diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index d851ec106cf4..28a9cbc07ab9 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -132,6 +132,9 @@ struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size, if (flags & DRM_TEGRA_GEM_CREATE_TILED) bo->flags |= TEGRA_BO_TILED; + if (flags & DRM_TEGRA_GEM_CREATE_BOTTOM_UP) + bo->flags |= TEGRA_BO_BOTTOM_UP; + return bo; err_mmap: diff --git a/drivers/gpu/drm/tegra/gem.h b/drivers/gpu/drm/tegra/gem.h index c4993fc2c3bd..7674000bf47d 100644 --- a/drivers/gpu/drm/tegra/gem.h +++ b/drivers/gpu/drm/tegra/gem.h @@ -24,7 +24,8 @@ #include #include -#define TEGRA_BO_TILED (1 << 0) +#define TEGRA_BO_TILED (1 << 0) +#define TEGRA_BO_BOTTOM_UP (1 << 1) struct tegra_bo { struct drm_gem_object gem; diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h index 6b420029a645..0f8575f58db8 100644 --- a/include/uapi/drm/tegra_drm.h +++ b/include/uapi/drm/tegra_drm.h @@ -19,7 +19,8 @@ #include -#define DRM_TEGRA_GEM_CREATE_TILED (1 << 0) +#define DRM_TEGRA_GEM_CREATE_TILED (1 << 0) +#define DRM_TEGRA_GEM_CREATE_BOTTOM_UP (1 << 1) struct drm_tegra_gem_create { __u64 size; -- cgit v1.2.3 From 8736fe81532182ba0086a371fae0708ea42a2cdf Mon Sep 17 00:00:00 2001 From: Arto Merilainen Date: Mon, 14 Oct 2013 15:21:52 +0300 Subject: gpu: host1x: Add 'flags' field to syncpt request Functions host1x_syncpt_request() and _host1x_syncpt_alloc() have been taking a separate boolean flag ('client_managed') for indicating if the syncpoint value should be tracked by the host1x driver. This patch converts the field into generic 'flags' field so that we can easily add more information while requesting a syncpoint. Clients are adapted to use the new interface accordingly. Signed-off-by: Arto Merilainen Reviewed-by: Terje Bergstrom Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/gr2d.c | 2 +- drivers/gpu/host1x/syncpt.c | 18 +++++++++++------- include/linux/host1x.h | 4 +++- 3 files changed, 15 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index 7f4eb110aa85..3a04b97b54a2 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c @@ -43,7 +43,7 @@ static int gr2d_init(struct host1x_client *client) if (!gr2d->channel) return -ENOMEM; - client->syncpts[0] = host1x_syncpt_request(client->dev, false); + client->syncpts[0] = host1x_syncpt_request(client->dev, 0); if (!client->syncpts[0]) { host1x_channel_free(gr2d->channel); return -ENOMEM; diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c index 03cf2922e469..5b88ba4c974e 100644 --- a/drivers/gpu/host1x/syncpt.c +++ b/drivers/gpu/host1x/syncpt.c @@ -30,9 +30,9 @@ #define SYNCPT_CHECK_PERIOD (2 * HZ) #define MAX_STUCK_CHECK_COUNT 15 -static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host, - struct device *dev, - bool client_managed) +static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host, + struct device *dev, + unsigned long flags) { int i; struct host1x_syncpt *sp = host->syncpt; @@ -51,7 +51,11 @@ static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host, sp->dev = dev; sp->name = name; - sp->client_managed = client_managed; + + if (flags & HOST1X_SYNCPT_CLIENT_MANAGED) + sp->client_managed = true; + else + sp->client_managed = false; return sp; } @@ -321,7 +325,7 @@ int host1x_syncpt_init(struct host1x *host) host1x_syncpt_restore(host); /* Allocate sync point to use for clearing waits for expired fences */ - host->nop_sp = _host1x_syncpt_alloc(host, NULL, false); + host->nop_sp = host1x_syncpt_alloc(host, NULL, 0); if (!host->nop_sp) return -ENOMEM; @@ -329,10 +333,10 @@ int host1x_syncpt_init(struct host1x *host) } struct host1x_syncpt *host1x_syncpt_request(struct device *dev, - bool client_managed) + unsigned long flags) { struct host1x *host = dev_get_drvdata(dev->parent); - return _host1x_syncpt_alloc(host, dev, client_managed); + return host1x_syncpt_alloc(host, dev, flags); } void host1x_syncpt_free(struct host1x_syncpt *sp) diff --git a/include/linux/host1x.h b/include/linux/host1x.h index f5dd56fbdc3e..eb713dbbae29 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -124,6 +124,8 @@ static inline void host1x_bo_kunmap(struct host1x_bo *bo, * host1x syncpoints */ +#define HOST1X_SYNCPT_CLIENT_MANAGED (1 << 0) + struct host1x_syncpt; struct host1x; @@ -135,7 +137,7 @@ int host1x_syncpt_incr(struct host1x_syncpt *sp); int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, u32 *value); struct host1x_syncpt *host1x_syncpt_request(struct device *dev, - bool client_managed); + unsigned long flags); void host1x_syncpt_free(struct host1x_syncpt *sp); /* -- cgit v1.2.3 From f5a954fed9b3eb04973ede72c50c66157fa9e15b Mon Sep 17 00:00:00 2001 From: Arto Merilainen Date: Mon, 14 Oct 2013 15:21:53 +0300 Subject: gpu: host1x: Add syncpoint base support This patch adds support for hardware syncpoint bases. This creates a simple mechanism to stall the command FIFO until an operation is completed. Signed-off-by: Arto Merilainen Reviewed-by: Terje Bergstrom Signed-off-by: Thierry Reding --- drivers/gpu/host1x/dev.h | 2 ++ drivers/gpu/host1x/hw/channel_hw.c | 20 +++++++++++ drivers/gpu/host1x/hw/hw_host1x01_uclass.h | 6 ++++ drivers/gpu/host1x/syncpt.c | 55 ++++++++++++++++++++++++++++-- drivers/gpu/host1x/syncpt.h | 6 ++++ include/linux/host1x.h | 5 +++ 6 files changed, 92 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h index 6cf689b9e17b..a61a976e7a42 100644 --- a/drivers/gpu/host1x/dev.h +++ b/drivers/gpu/host1x/dev.h @@ -27,6 +27,7 @@ #include "job.h" struct host1x_syncpt; +struct host1x_syncpt_base; struct host1x_channel; struct host1x_cdma; struct host1x_job; @@ -102,6 +103,7 @@ struct host1x { void __iomem *regs; struct host1x_syncpt *syncpt; + struct host1x_syncpt_base *bases; struct device *dev; struct clk *clk; diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c index 3be0cd296d3a..4608257ab656 100644 --- a/drivers/gpu/host1x/hw/channel_hw.c +++ b/drivers/gpu/host1x/hw/channel_hw.c @@ -67,6 +67,22 @@ static void submit_gathers(struct host1x_job *job) } } +static inline void synchronize_syncpt_base(struct host1x_job *job) +{ + struct host1x *host = dev_get_drvdata(job->channel->dev->parent); + struct host1x_syncpt *sp = host->syncpt + job->syncpt_id; + u32 id, value; + + value = host1x_syncpt_read_max(sp); + id = sp->base->id; + + host1x_cdma_push(&job->channel->cdma, + host1x_opcode_setclass(HOST1X_CLASS_HOST1X, + HOST1X_UCLASS_LOAD_SYNCPT_BASE, 1), + HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(id) | + HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(value)); +} + static int channel_submit(struct host1x_job *job) { struct host1x_channel *ch = job->channel; @@ -118,6 +134,10 @@ static int channel_submit(struct host1x_job *job) host1x_syncpt_read_max(sp))); } + /* Synchronize base register to allow using it for relative waiting */ + if (sp->base) + synchronize_syncpt_base(job); + syncval = host1x_syncpt_incr_max(sp, user_syncpt_incrs); job->syncpt_end = syncval; diff --git a/drivers/gpu/host1x/hw/hw_host1x01_uclass.h b/drivers/gpu/host1x/hw/hw_host1x01_uclass.h index 42f3ce19ca32..f7553599ee27 100644 --- a/drivers/gpu/host1x/hw/hw_host1x01_uclass.h +++ b/drivers/gpu/host1x/hw/hw_host1x01_uclass.h @@ -111,6 +111,12 @@ static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v) } #define HOST1X_UCLASS_WAIT_SYNCPT_BASE_OFFSET_F(v) \ host1x_uclass_wait_syncpt_base_offset_f(v) +static inline u32 host1x_uclass_load_syncpt_base_r(void) +{ + return 0xb; +} +#define HOST1X_UCLASS_LOAD_SYNCPT_BASE \ + host1x_uclass_load_syncpt_base_r() static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v) { return (v & 0xff) << 24; diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c index 5b88ba4c974e..159c479829c9 100644 --- a/drivers/gpu/host1x/syncpt.c +++ b/drivers/gpu/host1x/syncpt.c @@ -30,6 +30,29 @@ #define SYNCPT_CHECK_PERIOD (2 * HZ) #define MAX_STUCK_CHECK_COUNT 15 +static struct host1x_syncpt_base * +host1x_syncpt_base_request(struct host1x *host) +{ + struct host1x_syncpt_base *bases = host->bases; + unsigned int i; + + for (i = 0; i < host->info->nb_bases; i++) + if (!bases[i].requested) + break; + + if (i >= host->info->nb_bases) + return NULL; + + bases[i].requested = true; + return &bases[i]; +} + +static void host1x_syncpt_base_free(struct host1x_syncpt_base *base) +{ + if (base) + base->requested = false; +} + static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host, struct device *dev, unsigned long flags) @@ -44,6 +67,12 @@ static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host, if (i >= host->info->nb_pts) return NULL; + if (flags & HOST1X_SYNCPT_HAS_BASE) { + sp->base = host1x_syncpt_base_request(host); + if (!sp->base) + return NULL; + } + name = kasprintf(GFP_KERNEL, "%02d-%s", sp->id, dev ? dev_name(dev) : NULL); if (!name) @@ -307,20 +336,30 @@ int host1x_syncpt_patch_wait(struct host1x_syncpt *sp, void *patch_addr) int host1x_syncpt_init(struct host1x *host) { + struct host1x_syncpt_base *bases; struct host1x_syncpt *syncpt; int i; syncpt = devm_kzalloc(host->dev, sizeof(*syncpt) * host->info->nb_pts, - GFP_KERNEL); + GFP_KERNEL); if (!syncpt) return -ENOMEM; - for (i = 0; i < host->info->nb_pts; ++i) { + bases = devm_kzalloc(host->dev, sizeof(*bases) * host->info->nb_bases, + GFP_KERNEL); + if (!bases) + return -ENOMEM; + + for (i = 0; i < host->info->nb_pts; i++) { syncpt[i].id = i; syncpt[i].host = host; } + for (i = 0; i < host->info->nb_bases; i++) + bases[i].id = i; + host->syncpt = syncpt; + host->bases = bases; host1x_syncpt_restore(host); @@ -344,7 +383,9 @@ void host1x_syncpt_free(struct host1x_syncpt *sp) if (!sp) return; + host1x_syncpt_base_free(sp->base); kfree(sp->name); + sp->base = NULL; sp->dev = NULL; sp->name = NULL; sp->client_managed = false; @@ -398,3 +439,13 @@ struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id) return NULL; return host->syncpt + id; } + +struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp) +{ + return sp ? sp->base : NULL; +} + +u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base) +{ + return base->id; +} diff --git a/drivers/gpu/host1x/syncpt.h b/drivers/gpu/host1x/syncpt.h index 4eb933a497fd..9056465ecd3f 100644 --- a/drivers/gpu/host1x/syncpt.h +++ b/drivers/gpu/host1x/syncpt.h @@ -31,6 +31,11 @@ struct host1x; /* Reserved for replacing an expired wait with a NOP */ #define HOST1X_SYNCPT_RESERVED 0 +struct host1x_syncpt_base { + unsigned int id; + bool requested; +}; + struct host1x_syncpt { int id; atomic_t min_val; @@ -40,6 +45,7 @@ struct host1x_syncpt { bool client_managed; struct host1x *host; struct device *dev; + struct host1x_syncpt_base *base; /* interrupt data */ struct host1x_syncpt_intr intr; diff --git a/include/linux/host1x.h b/include/linux/host1x.h index eb713dbbae29..f5b9b87ac9a9 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -125,7 +125,9 @@ static inline void host1x_bo_kunmap(struct host1x_bo *bo, */ #define HOST1X_SYNCPT_CLIENT_MANAGED (1 << 0) +#define HOST1X_SYNCPT_HAS_BASE (1 << 1) +struct host1x_syncpt_base; struct host1x_syncpt; struct host1x; @@ -140,6 +142,9 @@ struct host1x_syncpt *host1x_syncpt_request(struct device *dev, unsigned long flags); void host1x_syncpt_free(struct host1x_syncpt *sp); +struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp); +u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base); + /* * host1x channel */ -- cgit v1.2.3 From c54a169b528d0ac2a5d3a7bf91f8534323bda83d Mon Sep 17 00:00:00 2001 From: Arto Merilainen Date: Mon, 14 Oct 2013 15:21:54 +0300 Subject: drm/tegra: Deliver syncpoint base to user space This patch adds a separate ioctl for delivering syncpoint base number to user space. If the syncpoint does not have an associated base, the function returns -ENXIO. Signed-off-by: Arto Merilainen Reviewed-by: Terje Bergstrom Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 29 +++++++++++++++++++++++++++++ include/uapi/drm/tegra_drm.h | 26 +++++++++++++++++--------- 2 files changed, 46 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index ecb6ec735ef1..28e178137718 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -418,6 +418,34 @@ static int tegra_submit(struct drm_device *drm, void *data, return context->client->ops->submit(context, args, drm, file); } + +static int tegra_get_syncpt_base(struct drm_device *drm, void *data, + struct drm_file *file) +{ + struct tegra_drm_file *fpriv = file->driver_priv; + struct drm_tegra_get_syncpt_base *args = data; + struct tegra_drm_context *context; + struct host1x_syncpt_base *base; + struct host1x_syncpt *syncpt; + + context = tegra_drm_get_context(args->context); + + if (!tegra_drm_file_owns_context(fpriv, context)) + return -ENODEV; + + if (args->syncpt >= context->client->base.num_syncpts) + return -EINVAL; + + syncpt = context->client->base.syncpts[args->syncpt]; + + base = host1x_syncpt_get_base(syncpt); + if (!base) + return -ENXIO; + + args->id = host1x_syncpt_base_id(base); + + return 0; +} #endif static const struct drm_ioctl_desc tegra_drm_ioctls[] = { @@ -431,6 +459,7 @@ static const struct drm_ioctl_desc tegra_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel, DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base, DRM_UNLOCKED), #endif }; diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h index 0f8575f58db8..5e1ab552cbed 100644 --- a/include/uapi/drm/tegra_drm.h +++ b/include/uapi/drm/tegra_drm.h @@ -68,6 +68,12 @@ struct drm_tegra_get_syncpt { __u32 id; }; +struct drm_tegra_get_syncpt_base { + __u64 context; + __u32 syncpt; + __u32 id; +}; + struct drm_tegra_syncpt { __u32 id; __u32 incrs; @@ -118,15 +124,16 @@ struct drm_tegra_submit { __u32 reserved[5]; /* future expansion */ }; -#define DRM_TEGRA_GEM_CREATE 0x00 -#define DRM_TEGRA_GEM_MMAP 0x01 -#define DRM_TEGRA_SYNCPT_READ 0x02 -#define DRM_TEGRA_SYNCPT_INCR 0x03 -#define DRM_TEGRA_SYNCPT_WAIT 0x04 -#define DRM_TEGRA_OPEN_CHANNEL 0x05 -#define DRM_TEGRA_CLOSE_CHANNEL 0x06 -#define DRM_TEGRA_GET_SYNCPT 0x07 -#define DRM_TEGRA_SUBMIT 0x08 +#define DRM_TEGRA_GEM_CREATE 0x00 +#define DRM_TEGRA_GEM_MMAP 0x01 +#define DRM_TEGRA_SYNCPT_READ 0x02 +#define DRM_TEGRA_SYNCPT_INCR 0x03 +#define DRM_TEGRA_SYNCPT_WAIT 0x04 +#define DRM_TEGRA_OPEN_CHANNEL 0x05 +#define DRM_TEGRA_CLOSE_CHANNEL 0x06 +#define DRM_TEGRA_GET_SYNCPT 0x07 +#define DRM_TEGRA_SUBMIT 0x08 +#define DRM_TEGRA_GET_SYNCPT_BASE 0x09 #define DRM_IOCTL_TEGRA_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_CREATE, struct drm_tegra_gem_create) #define DRM_IOCTL_TEGRA_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_MMAP, struct drm_tegra_gem_mmap) @@ -137,5 +144,6 @@ struct drm_tegra_submit { #define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_open_channel) #define DRM_IOCTL_TEGRA_GET_SYNCPT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT, struct drm_tegra_get_syncpt) #define DRM_IOCTL_TEGRA_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SUBMIT, struct drm_tegra_submit) +#define DRM_IOCTL_TEGRA_GET_SYNCPT_BASE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT_BASE, struct drm_tegra_get_syncpt_base) #endif -- cgit v1.2.3