[PATCHv5,RESEND 1/8] gpu: host1x: Add host1x driver
Thierry Reding
thierry.reding at avionic-design.de
Mon Feb 4 01:09:41 PST 2013
On Tue, Jan 15, 2013 at 01:43:57PM +0200, Terje Bergstrom wrote:
> Add host1x, the driver for host1x and its client unit 2D.
Maybe this could be a bit more verbose. Perhaps describe what host1x is.
> diff --git a/drivers/gpu/host1x/Kconfig b/drivers/gpu/host1x/Kconfig
[...]
> @@ -0,0 +1,6 @@
> +config TEGRA_HOST1X
> + tristate "Tegra host1x driver"
> + help
> + Driver for the Tegra host1x hardware.
Maybe s/Tegra/NVIDIA Tegra/?
> +
> + Required for enabling tegradrm.
This should probably be dropped. Either encode such knowledge as
explicit dependencies or in this case just remove it altogether since we
will probably merge both drivers anyway.
> diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
[...]
> +#include <linux/module.h>
> +#include <linux/list.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include "dev.h"
Maybe add a blank line between the previous two lines to visually
separate standard Linux includes from driver-specific ones.
> +#include "hw/host1x01.h"
> +
> +#define CREATE_TRACE_POINTS
> +#include <trace/events/host1x.h>
> +
> +#define DRIVER_NAME "tegra-host1x"
You only ever use this once, so maybe it can just be dropped?
> +static struct host1x_device_info host1x_info = {
Perhaps this should be host1x01_info in order to match the hardware
revision? That'll avoid it having to be renamed later on when other
revisions start to appear.
> +static int host1x_probe(struct platform_device *dev)
> +{
[...]
> + syncpt_irq = platform_get_irq(dev, 0);
> + if (IS_ERR_VALUE(syncpt_irq)) {
This is somewhat unusual. It should be fine to just do:
if (syncpt_irq < 0)
but IS_ERR_VALUE() should work fine too.
> + memcpy(&host->info, devid->data, sizeof(struct host1x_device_info));
Why not make the .info field a pointer to struct host1x_device_info
instead? That way you don't have to keep separate copies of the same
information.
> + /* set common host1x device data */
> + platform_set_drvdata(dev, host);
> +
> + host->regs = devm_request_and_ioremap(&dev->dev, regs);
> + if (!host->regs) {
> + dev_err(&dev->dev, "failed to remap host registers\n");
> + return -ENXIO;
> + }
This should probably be rewritten as:
host->regs = devm_ioremap_resource(&dev->dev, regs);
if (IS_ERR(host->regs))
return PTR_ERR(host->regs);
Though that function will only be available in 3.9-rc1.
> + err = host1x_syncpt_init(host);
> + if (err)
> + return err;
[...]
> + host1x_syncpt_reset(host);
Why separate host1x_syncpt_reset() from host1x_syncpt_init()? I see why
it might be useful to have host1x_syncpt_reset() as a separate function
but couldn't it be called as part of host1x_syncpt_init()?
> + dev_info(&dev->dev, "initialized\n");
I don't think this is very useful. We should make sure to tell people
when things fail. When everything goes as planned we don't need to brag
about it =)
> diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h
[...]
> +struct host1x_syncpt_ops {
[...]
> + const char * (*name)(struct host1x_syncpt *);
Why do we need this? Could we not refer to the syncpt name directly
instead of going through this wrapper? I'd expect the name to be static.
> +struct host1x_device_info {
Maybe this should be called simply host1x_info? _device seems redundant.
> + int nb_channels; /* host1x: num channels supported */
> + int nb_pts; /* host1x: num syncpoints supported */
> + int nb_bases; /* host1x: num syncpoints supported */
> + int nb_mlocks; /* host1x: number of mlocks */
> + int (*init)(struct host1x *); /* initialize per SoC ops */
> + int sync_offset;
> +};
While this isn't public API, maybe it would still be useful to turn the
comments into proper kerneldoc? That's what people are used to.
> +struct host1x {
> + void __iomem *regs;
> + struct host1x_syncpt *syncpt;
> + struct platform_device *dev;
> + struct host1x_device_info info;
> + struct clk *clk;
> +
> + struct host1x_syncpt_ops syncpt_op;
Maybe make this a struct host1x_syncpt_ops * instead so you don't have
separate copies? While at it, maybe this should be const as well.
> + struct dentry *debugfs;
This doesn't seem to be used anywhere.
> +static inline
> +struct host1x *host1x_get_host(struct platform_device *_dev)
> +{
> + struct platform_device *pdev;
> +
> + if (_dev->dev.parent) {
> + pdev = to_platform_device(_dev->dev.parent);
> + return platform_get_drvdata(pdev);
> + } else
> + return platform_get_drvdata(_dev);
> +}
There is a lot of needless casting in here. Why not pass in a struct
device * and use dev_get_drvdata() instead?
> diff --git a/drivers/gpu/host1x/hw/host1x01.c b/drivers/gpu/host1x/hw/host1x01.c
[...]
> +#include "hw/host1x01.h"
> +#include "dev.h"
> +#include "hw/host1x01_hardware.h"
The ordering here looks funny.
> +#include "hw/syncpt_hw.c"
Why include the source file here? Can't you compile it separately
instead?
> diff --git a/drivers/gpu/host1x/hw/host1x01.h b/drivers/gpu/host1x/hw/host1x01.h
[...]
> +int host1x01_init(struct host1x *);
For completeness you should probably name the parameter, even if this is
a prototype.
> diff --git a/drivers/gpu/host1x/hw/host1x01_hardware.h b/drivers/gpu/host1x/hw/host1x01_hardware.h
[...]
> +#include <linux/types.h>
> +#include <linux/bitops.h>
> +#include "hw_host1x01_sync.h"
Again, a blank line might help between the above two. I also assume that
this file will be filled with more content later on, so I guess it's not
worth the trouble to postpone it's creation until a later point.
> diff --git a/drivers/gpu/host1x/hw/hw_host1x01_sync.h b/drivers/gpu/host1x/hw/hw_host1x01_sync.h
[...]
> +static inline u32 host1x_sync_syncpt_0_r(void)
> +{
> + return 0x400;
> +}
> +#define HOST1X_SYNC_SYNCPT_0 \
> + host1x_sync_syncpt_0_r()
> +static inline u32 host1x_sync_syncpt_base_0_r(void)
> +{
> + return 0x600;
> +}
> +#define HOST1X_SYNC_SYNCPT_BASE_0 \
> + host1x_sync_syncpt_base_0_r()
> +static inline u32 host1x_sync_syncpt_cpu_incr_r(void)
> +{
> + return 0x700;
> +}
Perhaps it would be useful to modify these to take the syncpt ID as a
parameter? That way you don't have to remember to do the multiplication
everytime you access the register?
> diff --git a/drivers/gpu/host1x/hw/syncpt_hw.c b/drivers/gpu/host1x/hw/syncpt_hw.c
[...]
> +/*
> + * Updates the last value read from hardware.
> + * (was host1x_syncpt_load_min)
Can the comment in () not be dropped? Given that this is new code nobody
would know about the old name.
> + */
> +static u32 syncpt_load_min(struct host1x_syncpt *sp)
> +{
> + struct host1x *dev = sp->dev;
> + u32 old, live;
> +
> + do {
> + old = host1x_syncpt_read_min(sp);
> + live = host1x_sync_readl(dev,
> + HOST1X_SYNC_SYNCPT_0 + sp->id * 4);
> + } while ((u32)atomic_cmpxchg(&sp->min_val, old, live) != old);
I think this warrants a comment.
> + if (!host1x_syncpt_check_max(sp, live))
> + dev_err(&dev->dev->dev,
> + "%s failed: id=%u, min=%d, max=%d\n",
> + __func__,
> + sp->id,
> + host1x_syncpt_read_min(sp),
> + host1x_syncpt_read_max(sp));
You could probably make this fit into less lines.
> +/*
> + * Write a cpu syncpoint increment to the hardware, without touching
> + * the cache. Caller is responsible for host being powered.
> + */
The last part of this comment applies to every host1x function, right?
So maybe it should just be dropped.
> +static void syncpt_debug(struct host1x_syncpt *sp)
> +{
> + u32 i;
> + for (i = 0; i < host1x_syncpt_nb_pts(sp->dev); i++) {
> + u32 max = host1x_syncpt_read_max(sp);
> + u32 min = host1x_syncpt_load_min(sp);
> + if (!max && !min)
> + continue;
> + dev_info(&sp->dev->dev->dev,
> + "id %d (%s) min %d max %d\n",
> + i, sp->name,
> + min, max);
> +
> + }
There's a gratuitous blank line above.
> +
> + for (i = 0; i < host1x_syncpt_nb_bases(sp->dev); i++) {
> + u32 base_val;
> + host1x_syncpt_read_wait_base(sp);
> + base_val = sp->base_val;
> + if (base_val)
> + dev_info(&sp->dev->dev->dev,
> + "waitbase id %d val %d\n",
> + i, base_val);
> +
> + }
And another one.
> diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
[...]
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
I don't think this is needed.
> +#include <linux/module.h>
> +#include "syncpt.h"
> +#include "dev.h"
> +#include <trace/events/host1x.h>
Again, some more spacing would be nice here. And the ordering is a bit
weird. Maybe put the trace include above syncpt.h and dev.h?
> +#define MAX_SYNCPT_LENGTH 5
This doesn't seem to be used anywhere.
> +static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host,
> + struct platform_device *pdev,
> + int client_managed);
Can't you move the actual implementation here? Also I'm not sure if
passing the platform_device is the best choice here. struct device
should work just as well.
> +/*
> + * Resets syncpoint and waitbase values to sw shadows
> + */
> +void host1x_syncpt_reset(struct host1x *dev)
Maybe host1x_syncpt_flush() would be a better name given the above
description? Reset does have this hardware reset connotation so my first
intuition had been that this would reset the syncpt value to 0.
If you decide to change the name, make sure to change it in the syncpt
ops as well.
> +/*
> + * Updates sw shadow state for client managed registers
> + */
> +void host1x_syncpt_save(struct host1x *dev)
> +{
> + struct host1x_syncpt *sp_base = dev->syncpt;
> + u32 i;
> +
> + for (i = 0; i < host1x_syncpt_nb_pts(dev); i++) {
> + if (host1x_syncpt_client_managed(sp_base + i))
> + dev->syncpt_op.load_min(sp_base + i);
> + else
> + WARN_ON(!host1x_syncpt_min_eq_max(sp_base + i));
> + }
> +
> + for (i = 0; i < host1x_syncpt_nb_bases(dev); i++)
> + dev->syncpt_op.read_wait_base(sp_base + i);
> +}
A similar comment applies here. Though I'm not so sure about a better
name. Perhaps host1x_syncpt_sync()?
I know that this must sound all pretty straightforward to you, but for
somebody who hasn't used these functions at all the names are quite
confusing. So instead of people to go read the documentation I tend to
think that making the names as descriptive as possible is essential
here.
> +/*
> + * Updates the last value read from hardware.
> + */
> +u32 host1x_syncpt_load_min(struct host1x_syncpt *sp)
> +{
> + u32 val;
> + val = sp->dev->syncpt_op.load_min(sp);
> + trace_host1x_syncpt_load_min(sp->id, val);
> +
> + return val;
> +}
I don't know I understand what this means exactly. Does it read the
value that hardware last incremented? Perhaps this will become clearer
when you add a comment to the syncpt_load_min() implementation.
> +int host1x_syncpt_init(struct host1x *host)
> +{
> + struct host1x_syncpt *syncpt, *sp;
> + int i;
> +
> + syncpt = sp = devm_kzalloc(&host->dev->dev,
> + sizeof(struct host1x_syncpt) * host->info.nb_pts,
You can make this a bit shorter by using sizeof(*sp) instead.
> + for (i = 0; i < host->info.nb_pts; ++i, ++sp) {
> + sp->id = i;
> + sp->dev = host;
Perhaps:
syncpt[i].id = i;
syncpt[i].dev = host;
To avoid the need to explicitly keep track of sp?
> +static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host,
> + struct platform_device *pdev,
> + int client_managed)
> +{
> + int i;
> + struct host1x_syncpt *sp = host->syncpt;
> + char *name;
> +
> + for (i = 0; i < host->info.nb_pts && sp->name; i++, sp++)
> + ;
> + if (sp->pdev)
> + return NULL;
> +
> + name = kasprintf(GFP_KERNEL, "%02d-%s", sp->id,
> + pdev ? dev_name(&pdev->dev) : NULL);
> + if (!name)
> + return NULL;
> +
> + sp->pdev = pdev;
> + sp->name = name;
> + sp->client_managed = client_managed;
> +
> + return sp;
> +}
> +
> +struct host1x_syncpt *host1x_syncpt_alloc(struct platform_device *pdev,
> + int client_managed)
> +{
> + struct host1x *host = host1x_get_host(pdev);
> + return _host1x_syncpt_alloc(host, pdev, client_managed);
> +}
I think it's enough to keep track of the struct device here instead of
the struct platform_device.
Also the syncpoint is not actually allocated here, so maybe
host1x_syncpt_request() would be a better name. As a nice side-effect it
makes the naming more similar to the IRQ API and might be easier to work
with.
> +struct host1x_syncpt *host1x_syncpt_get(struct host1x *dev, u32 id)
> +{
> + return dev->syncpt + id;
> +}
Should this perhaps do some error checking. What if the specified syncpt
hasn't actually been requested before?
> diff --git a/drivers/gpu/host1x/syncpt.h b/drivers/gpu/host1x/syncpt.h
[...]
> +struct host1x_syncpt {
> + int id;
> + atomic_t min_val;
> + atomic_t max_val;
> + u32 base_val;
> + const char *name;
> + int client_managed;
Is this field actually ever used? Looking through the patches none of
the clients actually set this.
> +/*
> + * Returns true if syncpoint min == max, which means that there are no
> + * outstanding operations.
> + */
> +static inline bool host1x_syncpt_min_eq_max(struct host1x_syncpt *sp)
> +{
> + int min, max;
> + smp_rmb();
> + min = atomic_read(&sp->min_val);
> + max = atomic_read(&sp->max_val);
> + return (min == max);
> +}
Maybe call this host1x_syncpt_idle() or something similar instead?
> +{
> + return sp->id != NVSYNCPT_INVALID &&
> + sp->id < host1x_syncpt_nb_pts(sp->dev);
> +}
Is there really a need for NVSYNCPT_INVALID? Even if you want to keep
the special case you can drop the explicit check because -1 will be
larger than host1x_syncpt_nb_pts() anyway.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/dri-devel/attachments/20130204/84876489/attachment-0001.pgp>
More information about the dri-devel
mailing list