* Re: [PATCH 4/4] Adds ioctl interface support for ext4 project
From: lixi @ 2014-09-25 11:34 UTC (permalink / raw)
To: Dave Chinner
Cc: Jan Kara, Christoph Hellwig, Andreas Dilger,
linux-api-u79uwXL29TY76Z2rM5mHXA, xfs-VZNHf3L845pBDgjK7y7TUQ,
Dmitry Monakhov, viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn,
linux-fsdevel-u79uwXL29TY76Z2rM5mHXA, Theodore Ts'o,
Ext4 Developers List
In-Reply-To: <20140925075912.GG4758@dastard>
在 2014年9月25日,下午3:59,Dave Chinner <david-FqsqvQoI3Ljby3iVrkZq2A@public.gmane.org> 写道:
> On Wed, Sep 24, 2014 at 07:01:05PM +0200, Jan Kara wrote:
>> On Wed 24-09-14 09:26:34, Christoph Hellwig wrote:
>>> On Wed, Sep 24, 2014 at 06:25:07PM +0200, Jan Kara wrote:
>>>> On Wed 24-09-14 22:04:30, Li Xi wrote:
>>>>> This patch adds ioctl interface for setting/getting project of ext4.
>>>> The patch looks good to me. I was just wondering whether it won't be
>>>> useful to add an ioctl() which isn't ext4 specific. We could just extend
>>>> ->setattr() to allow setting of project ID (most filesystems would just
>>>> return -EOPNOTSUPP but ext4 and xfs could do the right thing) and then call
>>>> ->setattr from the generic ioctl. That way userspace won't have to care
>>>> about filesystem type when setting project ID... What do others think?
>>>
>>> Absolutely. In general I also wonder why this patch doesn't implement
>>> the full XFS API. Maybe there is a reason it was considered and
>>> rejected, but it would be helpful to document why.
>> Do you mean full get/setfsxattr API?
>
> That's a good start.
>
> The bigger issue in my mind is that we already have a fully featured
> quota API that supports project quotas and userspace tools available
> that manipulate it. xfstests already uses those tools and API
> for testing project quotas.
>
> This whole patchset reinvents all the quota APIs, and will require
> adding support in userspace, and hence require re-inventing all the
> test infrastructure we already have because it won't be compatible
> with the existing project quota test code.
>
>> That basically contains project ID,
>> flags (those that are currently get/set with FS_IOC_GETFLAGS/SETFLAGS), and
>> extent size hint right?
>
> It's a different set of flag definitions. We translate the interface
> XFS_XFLAG_* values to/from the inode on-disk XFS_DIFLAG_* inode values, just
> like we translate the VFS FS_*_FL flags that get passed through the
> FS_IOC_GETFLAGS/SETFLAGS ioctl.
>
>> That seems workable and it would also make setting
>> of PROJINHERIT flag fs agnostic. Only we would have to create some generic
>> flags namespace and merge into that ext4 flags and have a translation
>> function for the old ext4 flags.
>
> The XFS_XFLAGS_* are already filesystem agnostic - they are flags
> that are only used for interfacing with userspace and hence only
> exist at the ioctl copy in/out layer.....
>
>> Also I'm afraid we may quickly run out of
>> 32 available flags in xflags so we'd need to extend that. But all this
>> seems to be doable.
>
> The struct fsxattr was designed to be extensible - it has unused
> padding and enough space in the flags field to allow us to
> conditionally use that padding….
Hi Dave,
I was mostly working on the semantics of inherit flag on these patches. And
I didn’t realized that the interface differences would bother you so much. Sorry
for that.
I agree that we should choose a good interface. It would be good that it is
general so that it works well for all file systems. I agree that adding an
ext4 specific ioctl() is far from the best choice. I am willing to change it to
any general interface. A general ioctl() sounds good to me. Extend setattr()
and getattr() for project ID sounds even better, since project ID looks like
UID/GID. And general xattr name is another choice. But it is might be a little
bit confusing if we use xattr actually, since we are not saving project ID as
extended attribute on Ext4. Any choice is fine with me, as long as the
implementation won't introduce nasty codes or inconsistent design.
However, the problem is, I do not quite understand why we should keep
the interface exactly the same with XFS. It would be good if we can. But
as far as I can see, it seems hard. XFS uses a lot interfaces which are
not so standard and used by other file systems. For example, struct
fsxattr is not used by other file systems at all except XFS. I am not sure
why we should introduce this into Ext4 if there are a lot of other better
ways. I would be happy to change to XFS interfaces, if it is general.
However, I don’t think it is general enough.
I know xfstest is using the existing project quota interfaces of XFS. And
maybe there are some applications which are using them too. But
keeping the interfaces exactly the same with XFS would cost so much
effort that I’d like to get enough reasons before start working on it. Is it
really necessary? I am not so sure. It is so easy to change user
space applications comparing to changing a weird interfaces. For
example, I think it won’t cost even more than a day to add xfstest
support for new Ext4 project quota. And since project quota is far from
a widely used feature, I don’t think there is much compatibility problems
for existing applications. And If the new project interface are general
enough, there won’t be any compatibility problems for new applications
at all.
I might missed something important. So please let me know if you have
any advices.
Regards,
- Li Xi--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RFC v3 0/4] vfs: Non-blockling buffered fs read (page cache only)
From: Jan Kara @ 2014-09-25 11:16 UTC (permalink / raw)
To: Michael Kerrisk
Cc: Milosz Tanski, Linux Kernel, Christoph Hellwig, Linux-Fsdevel,
linux-aio, Mel Gorman, Volker Lendecke, Tejun Heo, Jeff Moyer,
Theodore Ts'o, Al Viro, Linux API
In-Reply-To: <CAHO5Pa2AbW+ndgUBjjB==NnwQjVuvbYaYdW8v0x-CjYYQEr9AA@mail.gmail.com>
On Thu 25-09-14 06:06:14, Michael Kerrisk wrote:
> Hello Milosz,
>
> On Wed, Sep 24, 2014 at 11:46 PM, Milosz Tanski <milosz@adfin.com> wrote:
> > This patcheset introduces an ability to perform a non-blocking read from
> > regular files in buffered IO mode. This works by only for those filesystems
> > that have data in the page cache.
> >
> > It does this by introducing new syscalls new syscalls preadv2/pwritev2. These
> > new syscalls behave like the network sendmsg, recvmsg syscalls that accept an
> > extra flag argument (RWF_NONBLOCK).
> >
> > It's a very common patern today (samba, libuv, etc..) use a large threadpool to
> > perform buffered IO operations. They submit the work form another thread
> > that performs network IO and epoll or other threads that perform CPU work. This
> > leads to increased latency for processing, esp. in the case of data that's
> > already cached in the page cache.
> >
> > With the new interface the applications will now be able to fetch the data in
> > their network / cpu bound thread(s) and only defer to a threadpool if it's not
> > there. In our own application (VLDB) we've observed a decrease in latency for
> > "fast" request by avoiding unnecessary queuing and having to swap out current
> > tasks in IO bound work threads.
>
> Since this is a change to the user-space API, could you CC future
> versions of this patch set to linux-api@vgerr.kernel.org please, as
There's typo in the address. It should be:
linux-api@vger.kernel.org
Honza
--
Jan Kara <jack@suse.cz>
SUSE Labs, CR
--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
^ permalink raw reply
* Re: [PATCH v2 1/2] perf: Add sampling of the raw monotonic clock
From: Pawel Moll @ 2014-09-25 10:49 UTC (permalink / raw)
To: Namhyung Kim
Cc: Richard Cochran, Steven Rostedt, Ingo Molnar, Peter Zijlstra,
Paul Mackerras, Arnaldo Carvalho de Melo, John Stultz,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <87sijhk21x.fsf-vfBCOVm4yAnB69T4xOojN9BPR1lH4CV8@public.gmane.org>
On Wed, 2014-09-24 at 06:41 +0100, Namhyung Kim wrote:
> Hi Pawel,
>
> On Tue, 23 Sep 2014 18:03:06 +0100, Pawel Moll wrote:
> > This patch adds an option to sample raw monotonic clock
> > value with any perf event, with the the aim of allowing
> > time correlation between data coming from perf and
> > additional performance-related information generated in
> > userspace.
> >
> > In order to correlate timestamps in perf data stream
> > with events happening in userspace (be it JITed debug
> > symbols or hwmon-originating environment data), user
> > requests a more or less periodic event (sched_switch
> > trace event of a hrtimer-based cpu-clock being the
> > most obvious examples) with PERF_SAMPLE_TIME *and*
> > PERF_SAMPLE_CLOCK_RAW_MONOTONIC and stamps
> > user-originating data with values obtained from
> > clock_gettime(CLOCK_MONOTONIC_RAW). Then, during
> > analysis, one looks at the perf events immediately
> > preceding and following (in terms of the
> > clock_raw_monotonic sample) the userspace event and
> > does simple linear approximation to get the equivalent
> > perf time.
> >
> > perf event user event
> > -----O--------------+-------------O------> t_mono
> > : | :
> > : V :
> > -----O----------------------------O------> t_perf
>
> Probably a dumb question: why not make PERF_SAMPLE_TIME being monotonic
> clock instead of adding a new PERF_SAMPLE_CLOCK_XXX flag?
It's a valid question. And it was asked before, in this thread:
http://thread.gmane.org/gmane.linux.kernel/1611683
A summary of the answer would be:
> On Wed, 2013-12-11 at 12:07 +0000, Ingo Molnar wrote:
> > * John Stultz <john.stultz-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> >
> > > [...]
> > >
> > > I'd much rather see perf export CLOCK_MONOTONIC_RAW timestamps,
> > > since that clockid is well defined. [...]
> >
> > So the problem with that clock is that it does the following for every
> > timestamp:
> >
> > cycle_now = clock->read(clock);
> >
> > ... which is impossibly slow if something like the HPET is used, which
> > is rather common - so this is a non-starter to timestamp perf events
> > with. We use the scheduler clock as a reasonable compromise between
> > scalability and clock globality.
Now, for your other comment:
> Maybe we can
> add a new ioctl command like PERF_EVENT_IOC_SET_CLOCK so that one can
> pass a clock id.
Did you mean selecting a time source for PERF_SAMPLE_TIME (so we don't
need two timestamps in a sample)? It would be doable, I guess, but what
if someone *wants* to have sched clock as the timestamps source (because
it's cheap) but still be able to correlate them with userspace? In this
case two separate timestamps are required to do the approximation.
Now, if you meant it to select a time source for the "other timestamp",
let's call it in a more generic way: PERF_SAMPLE_CLOCK_VALUE this could
work, yes. I see ALSA doing something similar (see
SNDRV_PCM_TSTAMP_TYPE_* enum in include/uapi/sound/asound.h). One
question would be: how does it work with groups? Does setting a
timesource for the leader selects them for all members? I don't think
you want a bunch of samples with different timestamp sources in the same
buffer.
Anyway, great stuff, thanks!
Pawel
^ permalink raw reply
* Re: [PATCH v5 1/3] drm/rockchip: Add basic drm driver
From: Mark yao @ 2014-09-25 8:58 UTC (permalink / raw)
To: Daniel Kurtz
Cc: Mark Rutland, Heiko Stübner, linux-doc, kever.yang,
dri-devel, linux-kernel@vger.kernel.org, xjq, zyw, linux-api,
jeff chen, linux-rockchip, kfx, Grant Likely, wxt, huangtao,
devicetree, Pawel Moll, Ian Campbell, yxj, Eddie Cai, Rob Herring,
Stéphane Marchesin, simon xue, xw, Greg Kroah-Hartman,
Randy Dunlap
In-Reply-To: <CAGS+omBKKktuqOfiek0VN5Nk6KGC64Yc7i_HbM=Wf3gyJZC8bw@mail.gmail.com>
[-- Attachment #1.1: Type: text/plain, Size: 132566 bytes --]
On 2014年09月25日 00:20, Daniel Kurtz wrote:
> Hi Mark,
>
> Please review comments inline...
>
> On Wed, Sep 24, 2014 at 10:12 AM, Mark yao <mark.yao@rock-chips.com> wrote:
>> This patch adds the basic structure of a DRM Driver for Rockchip Socs.
>>
>> Signed-off-by: Mark yao <mark.yao@rock-chips.com>
>> ---
>> Changes in v2:
>> - use the component framework to defer main drm driver probe
>> until all VOP devices have been probed.
>> - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
>> master device and each vop device can shared the drm dma mapping.
>> - use drm_crtc_init_with_planes and drm_universal_plane_init.
>> - remove unnecessary middle layers.
>> - add cursor set, move funcs to rockchip drm crtc.
>> - use vop reset at first init
>> - reference framebuffer when used and unreference when swap out vop
>>
>> Changes in v3:
>> - change "crtc->fb" to "crtc->primary-fb"
>> Adviced by Daniel Vetter
>> - init cursor plane with universal api, remove unnecessary cursor set,move
>>
>> Changes in v4:
>> Adviced by David Herrmann
>> - remove drm_platform_*() usage, use register drm device directly.
>> Adviced by Rob Clark
>> - remove special mmap ioctl, do userspace mmap with normal mmap() or mmap offset
>>
>> Changes in v5:
>> Adviced by Arnd Bergmann
>> - doing DMA start with a 32-bit masks with dma_mask and dma_coherent_mark
>> - fix some incorrect dependencies.
>> Adviced by Boris BREZILLON
>> - fix some mistake and bugs.
>> Adviced by Daniel Vetter
>> - drop all special ioctl and use generic kms ioctl instead.
>> Adviced by Rob Clark
>> - use unlocked api for drm_fb_helper_restore_fbdev_mode.
>> - remove unused rockchip_gem_prime_import_sg_table.
>>
>> drivers/gpu/drm/Kconfig | 2 +
>> drivers/gpu/drm/Makefile | 1 +
>> drivers/gpu/drm/rockchip/Kconfig | 17 +
>> drivers/gpu/drm/rockchip/Makefile | 8 +
>> drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 509 +++++++++
>> drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 120 +++
>> drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 201 ++++
>> drivers/gpu/drm/rockchip/rockchip_drm_fb.h | 28 +
>> drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 230 +++++
>> drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 20 +
>> drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 341 ++++++
>> drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 55 +
>> drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 1373 +++++++++++++++++++++++++
>> drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 187 ++++
>> 14 files changed, 3092 insertions(+)
>> create mode 100644 drivers/gpu/drm/rockchip/Kconfig
>> create mode 100644 drivers/gpu/drm/rockchip/Makefile
>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.h
>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.c
>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.h
>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>>
>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>> index b066bb3..7c4c3c6 100644
>> --- a/drivers/gpu/drm/Kconfig
>> +++ b/drivers/gpu/drm/Kconfig
>> @@ -171,6 +171,8 @@ config DRM_SAVAGE
>>
>> source "drivers/gpu/drm/exynos/Kconfig"
>>
>> +source "drivers/gpu/drm/rockchip/Kconfig"
>> +
>> source "drivers/gpu/drm/vmwgfx/Kconfig"
>>
>> source "drivers/gpu/drm/gma500/Kconfig"
>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>> index 4a55d59..d03387a 100644
>> --- a/drivers/gpu/drm/Makefile
>> +++ b/drivers/gpu/drm/Makefile
>> @@ -52,6 +52,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
>> obj-$(CONFIG_DRM_VIA) +=via/
>> obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
>> obj-$(CONFIG_DRM_EXYNOS) +=exynos/
>> +obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
>> obj-$(CONFIG_DRM_GMA500) += gma500/
>> obj-$(CONFIG_DRM_UDL) += udl/
>> obj-$(CONFIG_DRM_AST) += ast/
>> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
>> new file mode 100644
>> index 0000000..87255f7
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/Kconfig
>> @@ -0,0 +1,17 @@
>> +config DRM_ROCKCHIP
>> + tristate "DRM Support for Rockchip"
>> + depends on DRM && ROCKCHIP_IOMMU && ARM_DMA_USE_IOMMU && IOMMU_API
>> + select DRM_KMS_HELPER
>> + select DRM_KMS_FB_HELPER
>> + select DRM_PANEL
>> + select FB_CFB_FILLRECT
>> + select FB_CFB_COPYAREA
>> + select FB_CFB_IMAGEBLIT
>> + select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
>> + select VIDEOMODE_HELPERS
>> + help
>> + Choose this option if you have a Rockchip soc chipset.
>> + This driver provides kernel mode setting and buffer
>> + management to userspace. This driver does not provides
>> + 2D or 3D acceleration; acceleration is performed by other
>> + IP found on the SoC.
>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>> new file mode 100644
>> index 0000000..b3a5193
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -0,0 +1,8 @@
>> +#
>> +# Makefile for the drm device driver. This driver provides support for the
>> +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>> +
>> +rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
>> + rockchip_drm_gem.o rockchip_drm_vop.o
>> +
>> +obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> new file mode 100644
>> index 0000000..0ca4a6b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> @@ -0,0 +1,509 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * based on exynos_drm_drv.c
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#include <asm/dma-iommu.h>
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_fb_helper.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/of_graph.h>
>> +#include <linux/component.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_fb.h"
>> +#include "rockchip_drm_fbdev.h"
>> +#include "rockchip_drm_gem.h"
>> +
>> +#define DRIVER_NAME "rockchip"
>> +#define DRIVER_DESC "RockChip Soc DRM"
>> +#define DRIVER_DATE "20140818"
>> +#define DRIVER_MAJOR 1
>> +#define DRIVER_MINOR 0
>> +
>> +/*
>> + * Attach a (component) device to the shared drm dma mapping from master drm
>> + * device. This is used by the VOPs to map GEM buffers to a common DMA
>> + * mapping.
>> + */
>> +int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>> + struct device *dev)
>> +{
>> + struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
>> + int ret;
>> +
>> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
>> + if (ret)
>> + return ret;
>> +
>> + dma_set_max_seg_size(dev, 0xffffffffu);
>> +
>> + return arm_iommu_attach_device(dev, mapping);
>> +}
>> +
>> +void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>> + struct device *dev)
>> +{
>> + arm_iommu_detach_device(drm_dev->dev);
>> +}
>> +
>> +static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
>> +{
>> + struct rockchip_drm_private *private;
>> + struct dma_iommu_mapping *mapping;
>> + struct device *dev = drm_dev->dev;
>> + int ret;
>> +
>> + private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
>> + if (!private)
>> + return -ENOMEM;
>> +
>> + drm_dev->dev_private = private;
>> +
>> + drm_mode_config_init(drm_dev);
>> +
>> + rockchip_drm_mode_config_init(drm_dev);
>> +
>> + dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
>> + GFP_KERNEL);
>> + if (!dev->dma_parms) {
>> + ret = -ENOMEM;
>> + goto err_config_cleanup;
>> + }
>> +
>> + /* TODO(djkurtz): fetch the mapping start/size from somewhere */
>> + mapping = arm_iommu_create_mapping(&platform_bus_type, 0x10000000,
>> + SZ_1G);
>> + if (IS_ERR(mapping)) {
>> + ret = PTR_ERR(mapping);
>> + goto err_config_cleanup;
>> + }
>> +
>> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
>> + if (ret)
>> + goto err_config_cleanup;
> goto err_release_mapping;
oh, I make a mistake... Sure, I will fix it.
>
>> +
>> + dma_set_max_seg_size(dev, 0xffffffffu);
>> +
>> + ret = arm_iommu_attach_device(dev, mapping);
>> + if (ret)
>> + goto err_release_mapping;
>> +
>> + /* Try to bind all sub drivers. */
>> + ret = component_bind_all(dev, drm_dev);
>> + if (ret)
>> + goto err_detach_device;
>> +
>> + /* init kms poll for handling hpd */
>> + drm_kms_helper_poll_init(drm_dev);
>> +
>> + /*
>> + * enable drm irq mode.
>> + * - with irq_enabled = true, we can use the vblank feature.
>> + */
>> + drm_dev->irq_enabled = true;
>> +
>> + /*
>> + * with vblank_disable_allowed = true, vblank interrupt will be disabled
>> + * by drm timer once a current process gives up ownership of
>> + * vblank event.(after drm_vblank_put function is called)
>> + */
>> + drm_dev->vblank_disable_allowed = true;
>> +
>> + ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC);
>> + if (ret)
>> + goto err_kms_helper_poll_fini;
>> +
>> + rockchip_drm_fbdev_init(drm_dev);
>> +
>> + /* force connectors detection */
>> + drm_helper_hpd_irq_event(drm_dev);
>> +
>> + return 0;
>> +
>> +err_kms_helper_poll_fini:
>> + drm_kms_helper_poll_fini(drm_dev);
>> + component_unbind_all(dev, drm_dev);
>> +err_detach_device:
>> + arm_iommu_detach_device(dev);
>> +err_release_mapping:
>> + arm_iommu_release_mapping(dev->archdata.mapping);
>> +err_config_cleanup:
>> + drm_mode_config_cleanup(drm_dev);
>> + drm_dev->dev_private = NULL;
>> + return ret;
>> +}
>> +
>> +static int rockchip_drm_unload(struct drm_device *drm_dev)
>> +{
>> + struct device *dev = drm_dev->dev;
>> +
>> + drm_kms_helper_poll_fini(drm_dev);
>> + component_unbind_all(dev, drm_dev);
>> + arm_iommu_detach_device(dev);
>> + arm_iommu_release_mapping(dev->archdata.mapping);
>> + drm_mode_config_cleanup(drm_dev);
>> + drm_dev->dev_private = NULL;
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state)
>> +{
>> + struct drm_connector *connector;
>> +
>> + drm_modeset_lock_all(dev);
>> + list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
>> + int old_dpms = connector->dpms;
>> +
>> + if (connector->funcs->dpms)
>> + connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
>> +
>> + /* Set the old mode back to the connector for resume */
>> + connector->dpms = old_dpms;
>> + }
>> + drm_modeset_unlock_all(dev);
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_drm_resume(struct drm_device *dev)
>> +{
>> + struct drm_connector *connector;
>> +
>> + drm_modeset_lock_all(dev);
>> + list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
>> + if (connector->funcs->dpms)
>> + connector->funcs->dpms(connector, connector->dpms);
>> + }
>> + drm_modeset_unlock_all(dev);
>> +
>> + drm_helper_resume_force_mode(dev);
>> +
>> + return 0;
>> +}
>> +
>> +void rockchip_drm_lastclose(struct drm_device *dev)
>> +{
>> + struct rockchip_drm_private *priv = dev->dev_private;
>> +
>> + if (priv->fb_helper)
>> + drm_fb_helper_restore_fbdev_mode_unlocked(priv->fb_helper);
>> +}
>> +
>> +static const struct file_operations rockchip_drm_driver_fops = {
>> + .owner = THIS_MODULE,
>> + .open = drm_open,
>> + .mmap = rockchip_drm_gem_mmap,
>> + .poll = drm_poll,
>> + .read = drm_read,
>> + .unlocked_ioctl = drm_ioctl,
>> +#ifdef CONFIG_COMPAT
>> + .compat_ioctl = drm_compat_ioctl,
>> +#endif
>> + .release = drm_release,
>> +};
>> +
>> +const struct vm_operations_struct rockchip_drm_vm_ops = {
>> + .open = drm_gem_vm_open,
>> + .close = drm_gem_vm_close,
>> +};
>> +
>> +static struct drm_driver rockchip_drm_driver = {
>> + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
>> + .load = rockchip_drm_load,
>> + .unload = rockchip_drm_unload,
>> + .lastclose = rockchip_drm_lastclose,
>> + .suspend = rockchip_drm_suspend,
>> + .resume = rockchip_drm_resume,
> Looking at drm_sysfs.c, the .suspend/.resume callbacks are only
> invoked if the driver does not set DRIVER_MODESET in .driver_features.
> Since this driver does have DRIVER_MODESET, let's just drop these two items.
> The platform_driver provides its own suspend/resume callbacks, instead.
right, driver_features with DRIVER_MODESET, the suspend/resume do not be
called.
I will remove it to platform_driver.
>> + .get_vblank_counter = drm_vblank_count,
>> + .enable_vblank = rockchip_drm_crtc_enable_vblank,
>> + .disable_vblank = rockchip_drm_crtc_disable_vblank,
>> + .gem_vm_ops = &rockchip_drm_vm_ops,
>> + .gem_free_object = rockchip_gem_free_object,
>> + .dumb_create = rockchip_gem_dumb_create,
>> + .dumb_map_offset = rockchip_gem_dumb_map_offset,
>> + .dumb_destroy = drm_gem_dumb_destroy,
>> + .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
>> + .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
>> + .gem_prime_import = drm_gem_prime_import,
>> + .gem_prime_export = drm_gem_prime_export,
>> + .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
>> + .gem_prime_vmap = rockchip_gem_prime_vmap,
>> + .gem_prime_vunmap = rockchip_gem_prime_vunmap,
>> + .gem_prime_mmap = rockchip_gem_prime_mmap,
>> + .ioctls = rockchip_ioctls,
>> + .num_ioctls = ARRAY_SIZE(rockchip_ioctls),
>> + .fops = &rockchip_drm_driver_fops,
>> + .name = DRIVER_NAME,
>> + .desc = DRIVER_DESC,
>> + .date = DRIVER_DATE,
>> + .major = DRIVER_MAJOR,
>> + .minor = DRIVER_MINOR,
>> +};
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int rockchip_drm_sys_suspend(struct device *dev)
>> +{
>> + struct drm_device *drm_dev = dev_get_drvdata(dev);
>> + pm_message_t message;
>> +
>> + if (pm_runtime_suspended(dev))
>> + return 0;
>> +
>> + message.event = PM_EVENT_SUSPEND;
>> +
>> + return rockchip_drm_suspend(drm_dev, message);
>> +}
>> +
>> +static int rockchip_drm_sys_resume(struct device *dev)
>> +{
>> + struct drm_device *drm_dev = dev_get_drvdata(dev);
>> +
>> + if (pm_runtime_suspended(dev))
>> + return 0;
>> +
>> + return rockchip_drm_resume(drm_dev);
>> +}
>> +#endif
>> +
>> +static const struct dev_pm_ops rockchip_drm_pm_ops = {
>> + SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
>> + rockchip_drm_sys_resume)
>> +};
> Looks like this is not actually hooked up.
> Perhaps you meant to add this to rockchip_drm_platform_driver, see below...
>
>> +
>> +int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
>> + struct device_node *np)
>> +{
>> + struct rockchip_drm_private *priv = drm->dev_private;
>> + struct device_node *port;
>> + int pipe;
>> +
>> + if (priv->num_pipe >= ROCKCHIP_MAX_CRTC)
>> + return -EINVAL;
>> +
>> + port = of_get_child_by_name(np, "port");
>> + if (!port) {
>> + dev_err(drm->dev, "no port node found in %s\n",
>> + np->full_name);
>> + return -ENXIO;
>> + }
>> + pipe = priv->num_pipe++;
>> + crtc->port = port;
>> +
>> + priv->crtc[pipe] = crtc;
>> +
>> + return pipe;
>> +}
>> +
>> +void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe)
>> +{
>> + struct rockchip_drm_private *priv = drm->dev_private;
>> +
>> + priv->num_pipe--;
>> + of_node_put(priv->crtc[pipe]->port);
>> + priv->crtc[pipe] = NULL;
>> +}
>> +
>> +struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe)
>> +{
>> + struct rockchip_drm_private *priv = drm->dev_private;
>> +
>> + if (pipe < ROCKCHIP_MAX_CRTC && priv->crtc[pipe])
>> + return priv->crtc[pipe];
>> +
>> + return NULL;
>> +}
>> +
>> +/*
>> + * @node: device tree node containing encoder input ports
>> + * @encoder: drm_encoder
>> + */
>> +int rockchip_drm_encoder_get_mux_id(struct device_node *node,
>> + struct drm_encoder *encoder)
>> +{
>> + struct device_node *ep = NULL;
>> + struct drm_crtc *crtc = encoder->crtc;
>> + struct of_endpoint endpoint;
>> + struct device_node *port;
>> + int ret;
>> +
>> + if (!node || !crtc)
>> + return -EINVAL;
>> +
>> + do {
>> + ep = of_graph_get_next_endpoint(node, ep);
>> + if (!ep)
>> + break;
>> +
>> + port = of_graph_get_remote_port(ep);
>> + of_node_put(port);
>> + if (port == crtc->port) {
>> + ret = of_graph_parse_endpoint(ep, &endpoint);
>> + return ret ? ret : endpoint.id;
> nit:
>
> return ret ?: endpoint.id;
OK, I will do it.
>
>> + }
>> + } while (ep);
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static int compare_of(struct device *dev, void *data)
>> +{
>> + struct device_node *np = data;
>> +
>> + return dev->of_node == np;
>> +}
>> +
>> +static void rockchip_add_endpoints(struct device *dev,
>> + struct component_match **match,
>> + struct device_node *port)
>> +{
>> + struct device_node *ep, *remote;
>> +
>> + for_each_child_of_node(port, ep) {
>> + remote = of_graph_get_remote_port_parent(ep);
>> + if (!remote || !of_device_is_available(remote)) {
>> + of_node_put(remote);
>> + continue;
>> + } else if (!of_device_is_available(remote->parent)) {
>> + dev_warn(dev, "parent device of %s is not available\n",
>> + remote->full_name);
>> + of_node_put(remote);
>> + continue;
>> + }
>> +
>> + component_match_add(dev, match, compare_of, remote);
>> + of_node_put(remote);
>> + }
>> +}
>> +
>> +static int rockchip_drm_bind(struct device *dev)
>> +{
>> + struct drm_device *drm;
>> + int ret;
>> +
>> + drm = drm_dev_alloc(&rockchip_drm_driver, dev);
>> + if (!drm)
>> + return -ENOMEM;
>> +
>> + ret = drm_dev_set_unique(drm, "%s", dev_name(dev));
>> + if (ret)
>> + goto err_free;
>> +
>> + ret = drm_dev_register(drm, 0);
>> + if (ret)
>> + goto err_free;
>> +
>> + dev_set_drvdata(dev, drm);
>> +
>> + return 0;
>> +
>> +err_free:
>> + drm_dev_unref(drm);
>> + return ret;
>> +}
>> +
>> +static void rockchip_drm_unbind(struct device *dev)
>> +{
>> + struct drm_device *drm = dev_get_drvdata(dev);
>> +
>> + drm_dev_unregister(drm);
>> + drm_dev_unref(drm);
>> +}
>> +
>> +static const struct component_master_ops rockchip_drm_ops = {
>> + .bind = rockchip_drm_bind,
>> + .unbind = rockchip_drm_unbind,
>> +};
>> +
>> +static int rockchip_drm_platform_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct component_match *match = NULL;
>> + struct device_node *np = dev->of_node;
>> + struct device_node *port;
>> + int i;
>> + int ret;
>> +
>> + if (!np)
>> + return -ENODEV;
>> + /*
>> + * Bind the crtc ports first, so that
>> + * drm_of_find_possible_crtcs called from encoder .bind callbacks
>> + * works as expected.
>> + */
>> + for (i = 0;; i++) {
>> + port = of_parse_phandle(np, "ports", i);
>> + if (!port)
>> + break;
> I think you want to make sure that the parent node of this 'port' (ie,
> the VOP node) is not disabled...
>
> if (!of_device_is_available(port->parent)) {
> of_node_put(port);
> continue;
> }
right, vop node maybe disabled, we should check it.
>> +
>> + component_match_add(dev, &match, compare_of, port->parent);
>> + of_node_put(port);
>> + }
>> +
>> + if (i == 0) {
>> + dev_err(dev, "missing 'ports' property\n");
>> + return -ENODEV;
>> + }
>> + /*
>> + * For each bound crtc, bind the encoders attached to its
>> + * remote endpoint.
>> + */
>> + for (i = 0;; i++) {
>> + port = of_parse_phandle(np, "ports", i);
>> + if (!port)
>> + break;
>> +
> Here, too:
> if (!of_device_is_available(port->parent)) {
> of_node_put(port);
> continue;
> }
OK.
>> + rockchip_add_endpoints(dev, &match, port);
>> + of_node_put(port);
>> + }
>> +
>> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
>> + if (ret)
>> + return ret;
>> +
>> + return component_master_add_with_match(dev, &rockchip_drm_ops, match);
>> +}
>> +
>> +static int rockchip_drm_platform_remove(struct platform_device *pdev)
>> +{
>> + component_master_del(&pdev->dev, &rockchip_drm_ops);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id rockchip_drm_dt_ids[] = {
>> + { .compatible = "rockchip,display-subsystem", },
>> + { /* sentinel */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
>> +
>> +static struct platform_driver rockchip_drm_platform_driver = {
>> + .probe = rockchip_drm_platform_probe,
>> + .remove = rockchip_drm_platform_remove,
>> + .driver = {
>> + .owner = THIS_MODULE,
>> + .name = "rockchip-drm",
>> + .of_match_table = rockchip_drm_dt_ids,
> .pm = &rockchip_drm_pm_ops,
OK
>> + },
>> +};
>> +
>> +module_platform_driver(rockchip_drm_platform_driver);
>> +
>> +MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
>> +MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> new file mode 100644
>> index 0000000..154b3ec
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> @@ -0,0 +1,120 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * based on exynos_drm_drv.h
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef _ROCKCHIP_DRM_DRV_H
>> +#define _ROCKCHIP_DRM_DRV_H
>> +
>> +#include <linux/module.h>
>> +#include <linux/component.h>
>> +
>> +#define ROCKCHIP_MAX_FB_BUFFER 4
> This is only used to size the array of gem object pointers used by a
> framebuffer .
> What framebuffer pixel format uses 4 planes? I think the max is 3?
right, the max is 3 with YUV format.
>> +#define ROCKCHIP_MAX_CONNECTOR 2
>> +
>> +struct drm_device;
>> +struct drm_connector;
>> +
>> +/*
>> + * display output interface supported by rockchip lcdc
> s/lcdc/vop
OK, I will fix it.
>> + */
>> +#define ROCKCHIP_OUTFACE_P888 0
>> +#define ROCKCHIP_OUTFACE_P666 1
>> +#define ROCKCHIP_OUTFACE_P565 2
>> +/* for use special outface */
>> +#define ROCKCHIP_OUTFACE_AAAA 15
> "outface" is creative. It is actually an English word that means "to
> disconcert an opponent by bold confrontation", I do not think that is
> what you mean here :-).
>
> Rename these to ROCKCHIP_OUT_MODE_, to be consistent with the
> corresponding field (dsp_out_mode) of the VOP_DSP_CTRL0 register to
> which these are written.
> Also, since these are register values written to VOP_DSP_CTRL0, so
> move them to the VOP header file.
"outface" maybe Chinese English, so you are right, I will change it.
>> +
>> +#define ROCKCHIP_COLOR_SWAP_RG 0x1
>> +#define ROCKCHIP_COLOR_SWAP_RB 0x2
>> +#define ROCKCHIP_COLOR_SWAP_GB 0x4
>> +
>> +/*
>> + * Special mode info for rockchip
>> + *
>> + * @out_type: lcd controller need to know the sceen type.
>> + */
>> +struct rockchip_display_mode {
>> + int out_type;
>> +};
>> +
>> +#define ROCKCHIP_EVENT_HOTPLUG 1
>> +
>> +enum rockchip_plane_type {
>> + ROCKCHIP_WIN0,
>> + ROCKCHIP_WIN1,
>> + ROCKCHIP_WIN2,
>> + ROCKCHIP_WIN3,
>> + ROCKCHIP_CURSOR,
>> + ROCKCHIP_MAX_PLANE,
>> +};
>> +
>> +/* This enumerates device type. */
>> +enum rockchip_drm_device_type {
>> + ROCKCHIP_DEVICE_TYPE_NONE,
>> + ROCKCHIP_DEVICE_TYPE_CRTC,
>> + ROCKCHIP_DEVICE_TYPE_CONNECTOR,
>> +};
>> +
>> +/* this enumerates display type. */
>> +enum rockchip_drm_output_type {
>> + ROCKCHIP_DISPLAY_TYPE_NONE = 0,
> To match the enum name, use ROCKCHIP_OUTPUT_TYPE_*.
> Also, no need to explicitly set the first one to 0.
> However, see below. I don't think we to modify the drm_display_mode
> to include an output type.
but vop devices need know the connector type, connector enable register
is in vop.
can I do that like under to get connector type for crtc?
static int rockchip_get_connector_type(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_connector * connector;
list_for_each_entry(connector,
&dev->mode_config.connector_list, head) {
if (!connector->encoder)
continue;
/*
* one crtc only has one connector in my case, so just find
the first connector at list.
*/
if (connector->encoder->crtc == crtc)
break;
}
if (!connector)
return -EINVAL;
return connector->connector_type;
}
>> + /* RGB Interface. */
>> + ROCKCHIP_DISPLAY_TYPE_RGB,
>> + /* LVDS Interface. */
>> + ROCKCHIP_DISPLAY_TYPE_LVDS,
>> + /* EDP Interface. */
>> + ROCKCHIP_DISPLAY_TYPE_EDP,
>> + /* MIPI Interface. */
>> + ROCKCHIP_DISPLAY_TYPE_MIPI,
>> + /* HDMI Interface. */
>> + ROCKCHIP_DISPLAY_TYPE_HDMI,
>> +};
>> +
>> +enum rockchip_crtc_type {
>> + ROCKCHIP_CRTC_VOPB,
>> + ROCKCHIP_CRTC_VOPL,
>> + ROCKCHIP_MAX_CRTC,
>> +};
> None of the following defined above are used and they can all be removed:
> ROCKCHIP_EVENT_HOTPLUG
> rockchip_plane_type
> rockchip_drm_device_type
> ROCKCHIP_COLOR_SWAP_*
> rockchip_crtc_type
OK, I will drop them.
>> +
>> +/*
>> + * Rockchip drm private structure.
>> + *
>> + * @num_pipe: number of pipes for this device.
>> + */
>> +struct rockchip_drm_private {
>> + struct drm_fb_helper *fb_helper;
>> + /*
>> + * created crtc object would be contained at this array and
>> + * this array is used to be aware of which crtc did it request vblank.
>> + */
> Move this comment up to the struct comment, above "num_pipe".
>
> @crtc: array of enabled CRTCs, used to map from "pipe" to drm_crtc
OK
>> + struct drm_crtc *crtc[ROCKCHIP_MAX_CRTC];
>> +
>> + unsigned int num_pipe;
>> +};
>> +
>> +int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
>> + struct device_node *port);
>> +void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe);
>> +struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe);
> For consistency, this should probably be "rockchip_drm_find_crtc",
> although you can probably drop the "_drm" from all of these function
> names if you wanted to make them shorter.
> Or, perhaps use: rk_drm_*()
OK
>> +int rockchip_drm_encoder_get_mux_id(struct device_node *node,
>> + struct drm_encoder *encoder);
>> +void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
>> +void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev);
>> +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
>> +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
>> +int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>> + struct device *dev);
>> +void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>> + struct device *dev);
> A blank line here helps.
OK
>> +#endif /* _ROCKCHIP_DRM_DRV_H_ */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> new file mode 100644
>> index 0000000..482f7b8
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> @@ -0,0 +1,201 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <drm/drm.h>
>> +#include <drm/drmP.h>
>> +#include <drm/drm_fb_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_gem.h"
>> +
>> +#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>> +
>> +struct rockchip_drm_fb {
>> + struct drm_framebuffer fb;
>> + struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
>> +};
>> +
>> +struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
>> + unsigned int plane)
>> +{
>> + struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
>> +
>> + if (plane >= ROCKCHIP_MAX_FB_BUFFER)
>> + return NULL;
>> +
>> + return rk_fb->obj[plane];
>> +}
>> +
>> +static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
>> +{
>> + struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
>> + struct drm_gem_object *obj;
>> + int i;
>> +
>> + for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
>> + obj = rockchip_fb->obj[i];
>> + if (obj)
>> + drm_gem_object_unreference_unlocked(obj);
>> + }
>> +
>> + drm_framebuffer_cleanup(fb);
>> + kfree(rockchip_fb);
>> +}
>> +
>> +static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>> + struct drm_file *file_priv,
>> + unsigned int *handle)
>> +{
>> + struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
>> +
>> + return drm_gem_handle_create(file_priv,
>> + rockchip_fb->obj[0], handle);
>> +}
>> +
>> +static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>> + .destroy = rockchip_drm_fb_destroy,
>> + .create_handle = rockchip_drm_fb_create_handle,
>> +};
>> +
>> +static struct rockchip_drm_fb *
>> +rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
>> + struct drm_gem_object **obj, unsigned int num_planes)
>> +{
>> + struct rockchip_drm_fb *rockchip_fb;
>> + int ret;
>> + int i;
>> +
>> + rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
>> + if (!rockchip_fb)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
>> +
>> + for (i = 0; i < num_planes; i++)
>> + rockchip_fb->obj[i] = obj[i];
>> +
>> + ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
>> + &rockchip_drm_fb_funcs);
>> + if (ret) {
>> + dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
>> + ret);
>> + kfree(rockchip_fb);
>> + return ERR_PTR(ret);
>> + }
>> +
>> + return rockchip_fb;
>> +}
>> +
>> +static struct drm_framebuffer *
>> +rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
>> + struct drm_mode_fb_cmd2 *mode_cmd)
>> +{
>> + struct rockchip_drm_fb *rockchip_fb;
>> + struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
>> + struct drm_gem_object *obj;
>> + unsigned int hsub;
>> + unsigned int vsub;
>> + int num_planes;
>> + int ret;
>> + int i;
>> +
>> + hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
>> + vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
>> + num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
>> + ROCKCHIP_MAX_FB_BUFFER);
>> +
>> + for (i = 0; i < num_planes; i++) {
>> + unsigned int width = mode_cmd->width / (i ? hsub : 1);
>> + unsigned int height = mode_cmd->height / (i ? vsub : 1);
>> + unsigned int min_size;
>> +
>> + obj = drm_gem_object_lookup(dev, file_priv,
>> + mode_cmd->handles[i]);
>> + if (!obj) {
>> + dev_err(dev->dev, "Failed to lookup GEM object\n");
>> + ret = -ENXIO;
>> + goto err_gem_object_unreference;
>> + }
>> +
>> + min_size = (height - 1) * mode_cmd->pitches[i] +
>> + mode_cmd->offsets[i] +
>> + width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
>> +
>> + if (obj->size < min_size) {
>> + drm_gem_object_unreference_unlocked(obj);
>> + ret = -EINVAL;
>> + goto err_gem_object_unreference;
>> + }
>> + objs[i] = obj;
>> + }
>> +
>> + rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
>> + if (IS_ERR(rockchip_fb)) {
>> + ret = PTR_ERR(rockchip_fb);
>> + goto err_gem_object_unreference;
>> + }
>> +
>> + return &rockchip_fb->fb;
>> +
>> +err_gem_object_unreference:
>> + for (i--; i >= 0; i--)
>> + drm_gem_object_unreference_unlocked(objs[i]);
>> + return ERR_PTR(ret);
>> +}
>> +
>> +static void rockchip_drm_output_poll_changed(struct drm_device *dev)
>> +{
>> + struct rockchip_drm_private *private = dev->dev_private;
>> + struct drm_fb_helper *fb_helper = private->fb_helper;
>> +
>> + if (fb_helper)
>> + drm_fb_helper_hotplug_event(fb_helper);
>> +}
>> +
>> +static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
>> + .fb_create = rockchip_user_fb_create,
>> + .output_poll_changed = rockchip_drm_output_poll_changed,
>> +};
>> +
>> +struct drm_framebuffer *
>> +rockchip_drm_framebuffer_init(struct drm_device *dev,
>> + struct drm_mode_fb_cmd2 *mode_cmd,
>> + struct drm_gem_object *obj)
>> +{
>> + struct rockchip_drm_fb *rockchip_fb;
>> +
>> + rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
>> + if (IS_ERR(rockchip_fb))
>> + return NULL;
>> +
>> + return &rockchip_fb->fb;
>> +}
>> +
>> +void rockchip_drm_mode_config_init(struct drm_device *dev)
>> +{
>> + dev->mode_config.min_width = 0;
>> + dev->mode_config.min_height = 0;
>> +
>> + /*
>> + * set max width and height as default value(4096x4096).
>> + * this value would be used to check framebuffer size limitation
>> + * at drm_mode_addfb().
>> + */
>> + dev->mode_config.max_width = 4096;
>> + dev->mode_config.max_height = 4096;
>> +
>> + dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
>> +}
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
>> new file mode 100644
>> index 0000000..09574d4
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
>> @@ -0,0 +1,28 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef _ROCKCHIP_DRM_FB_H
>> +#define _ROCKCHIP_DRM_FB_H
>> +
>> +struct drm_framebuffer *
>> +rockchip_drm_framebuffer_init(struct drm_device *dev,
>> + struct drm_mode_fb_cmd2 *mode_cmd,
>> + struct drm_gem_object *obj);
>> +void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb);
>> +
>> +void rockchip_drm_mode_config_init(struct drm_device *dev);
>> +
>> +struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
>> + unsigned int plane);
>> +#endif /* _ROCKCHIP_DRM_FB_H */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
>> new file mode 100644
>> index 0000000..ae563f5
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
>> @@ -0,0 +1,230 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#include <drm/drm.h>
>> +#include <drm/drmP.h>
>> +#include <drm/drm_fb_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_gem.h"
>> +#include "rockchip_drm_fb.h"
>> +
>> +#define PREFERRED_BPP 32
>> +#define to_rockchip_fbdev(x) container_of(x, struct rockchip_fbdev, helper)
>> +
>> +struct rockchip_fbdev {
>> + struct drm_fb_helper helper;
>> + struct drm_gem_object *bo;
>> +};
>> +
>> +static int rockchip_fbdev_mmap(struct fb_info *info,
>> + struct vm_area_struct *vma)
>> +{
>> + struct drm_fb_helper *helper = info->par;
>> + struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
>> +
>> + return rockchip_gem_mmap(fbdev->bo, vma);
>> +}
>> +
>> +static struct fb_ops rockchip_drm_fbdev_ops = {
>> + .owner = THIS_MODULE,
>> + .fb_mmap = rockchip_fbdev_mmap,
>> + .fb_fillrect = cfb_fillrect,
>> + .fb_copyarea = cfb_copyarea,
>> + .fb_imageblit = cfb_imageblit,
>> + .fb_check_var = drm_fb_helper_check_var,
>> + .fb_set_par = drm_fb_helper_set_par,
>> + .fb_blank = drm_fb_helper_blank,
>> + .fb_pan_display = drm_fb_helper_pan_display,
>> + .fb_setcmap = drm_fb_helper_setcmap,
>> +};
>> +
>> +static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
>> + struct drm_fb_helper_surface_size *sizes)
>> +{
>> + struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
>> + struct drm_mode_fb_cmd2 mode_cmd = { 0 };
>> + struct drm_device *dev = helper->dev;
>> + struct rockchip_gem_object *rk_obj;
>> + struct drm_framebuffer *fb;
>> + unsigned int bytes_per_pixel;
>> + unsigned long offset;
>> + struct fb_info *fbi;
>> + size_t size;
>> + int ret;
>> +
>> + bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
>> +
>> + mode_cmd.width = sizes->surface_width;
>> + mode_cmd.height = sizes->surface_height;
>> + mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
>> + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
>> + sizes->surface_depth);
>> +
>> + size = mode_cmd.pitches[0] * mode_cmd.height;
>> +
>> + rk_obj = rockchip_gem_create_object(dev, size);
>> + if (IS_ERR(rk_obj))
>> + return -ENOMEM;
>> +
>> + fbdev->bo = &rk_obj->base;
>> +
>> + fbi = framebuffer_alloc(0, dev->dev);
>> + if (!fbi) {
>> + dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
>> + ret = -ENOMEM;
>> + goto err_rockchip_gem_free_object;
>> + }
>> +
>> + helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd, fbdev->bo);
>> + if (IS_ERR(helper->fb)) {
>> + dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
>> + ret = PTR_ERR(helper->fb);
>> + goto err_framebuffer_release;
>> + }
>> +
>> + helper->fbdev = fbi;
>> +
>> + fbi->par = helper;
>> + fbi->flags = FBINFO_FLAG_DEFAULT;
>> + fbi->fbops = &rockchip_drm_fbdev_ops;
>> +
>> + ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
>> + if (ret) {
>> + dev_err(dev->dev, "Failed to allocate color map.\n");
>> + goto err_drm_framebuffer_unref;
>> + }
>> +
>> + fb = helper->fb;
>> + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
>> + drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
>> +
>> + offset = fbi->var.xoffset * bytes_per_pixel;
>> + offset += fbi->var.yoffset * fb->pitches[0];
>> +
>> + dev->mode_config.fb_base = 0;
>> + fbi->screen_base = rk_obj->kvaddr + offset;
>> + fbi->screen_size = rk_obj->base.size;
>> + fbi->fix.smem_len = rk_obj->base.size;
>> +
>> + DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n",
>> + fb->width, fb->height, fb->depth, rk_obj->kvaddr,
>> + offset, size);
>> + return 0;
>> +
>> +err_drm_framebuffer_unref:
>> + drm_framebuffer_unreference(helper->fb);
>> +err_framebuffer_release:
>> + framebuffer_release(fbi);
>> +err_rockchip_gem_free_object:
>> + rockchip_gem_free_object(&rk_obj->base);
>> + return ret;
>> +}
>> +
>> +static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
>> + .fb_probe = rockchip_drm_fbdev_create,
>> +};
>> +
>> +int rockchip_drm_fbdev_init(struct drm_device *dev)
>> +{
>> + struct rockchip_drm_private *private = dev->dev_private;
>> + struct rockchip_fbdev *fbdev;
>> + struct drm_fb_helper *helper;
>> + unsigned int num_crtc;
>> + int ret;
>> +
>> + if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
>> + return -EINVAL;
>> +
>> + if (private->fb_helper) {
>> + DRM_ERROR("no allow to reinit fbdev\n");
>> + return -EINVAL;
>> + }
>> +
>> + num_crtc = dev->mode_config.num_crtc;
>> +
>> + fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
>> + if (!fbdev)
>> + return -ENOMEM;
>> +
>> + fbdev->helper.funcs = &rockchip_drm_fb_helper_funcs;
>> + helper = &fbdev->helper;
>> +
>> + ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR);
>> + if (ret < 0) {
>> + dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
>> + goto err_free;
>> + }
>> +
>> + ret = drm_fb_helper_single_add_all_connectors(helper);
>> + if (ret < 0) {
>> + dev_err(dev->dev, "Failed to add connectors.\n");
>> + goto err_drm_fb_helper_fini;
>> + }
>> +
>> + /* disable all the possible outputs/crtcs before entering KMS mode */
>> + drm_helper_disable_unused_functions(dev);
>> +
>> + ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
>> + if (ret < 0) {
>> + dev_err(dev->dev, "Failed to set initial hw configuration.\n");
>> + goto err_drm_fb_helper_fini;
>> + }
>> +
>> + private->fb_helper = helper;
>> +
>> + return 0;
>> +
>> +err_drm_fb_helper_fini:
>> + drm_fb_helper_fini(helper);
>> +err_free:
>> + kfree(fbdev);
>> + return ret;
>> +}
>> +
>> +void rockchip_drm_fbdev_fini(struct drm_device *dev)
>> +{
>> + struct rockchip_drm_private *private = dev->dev_private;
>> + struct drm_fb_helper *helper;
>> + struct rockchip_fbdev *fbdev;
>> +
>> + if (!private || !private->fb_helper)
>> + return;
>> +
>> + helper = private->fb_helper;
>> + fbdev = to_rockchip_fbdev(helper);
>> +
>> + if (helper->fbdev) {
>> + struct fb_info *info;
>> + int ret;
>> +
>> + info = helper->fbdev;
>> + ret = unregister_framebuffer(info);
>> + if (ret < 0)
>> + DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
>> +
>> + if (info->cmap.len)
>> + fb_dealloc_cmap(&info->cmap);
>> +
>> + framebuffer_release(info);
>> + }
>> +
>> + if (helper->fb)
>> + drm_framebuffer_unreference(helper->fb);
>> +
>> + drm_fb_helper_fini(helper);
>> + kfree(fbdev);
>> + private->fb_helper = NULL;
>> +}
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
>> new file mode 100644
>> index 0000000..5edcf6a
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
>> @@ -0,0 +1,20 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef _ROCKCHIP_DRM_FBDEV_H
>> +#define _ROCKCHIP_DRM_FBDEV_H
>> +
>> +int rockchip_drm_fbdev_init(struct drm_device *dev);
>> +
>> +#endif /* _ROCKCHIP_DRM_FBDEV_H */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
>> new file mode 100644
>> index 0000000..8e7a91c
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
>> @@ -0,0 +1,341 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#include <drm/drm.h>
>> +#include <drm/drmP.h>
>> +#include <drm/drm_vma_manager.h>
>> +
>> +#include <linux/anon_inodes.h>
>> +#include <linux/dma-attrs.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_gem.h"
>> +
>> +static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj)
>> +{
>> + struct drm_gem_object *obj = &rk_obj->base;
>> + struct drm_device *drm = obj->dev;
>> +
>> + init_dma_attrs(&rk_obj->dma_attrs);
>> + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
>> +
>> + /* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */
>> + rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
>> + &rk_obj->dma_addr, GFP_KERNEL,
>> + &rk_obj->dma_attrs);
>> + if (IS_ERR(rk_obj->kvaddr)) {
>> + int ret = PTR_ERR(rk_obj->kvaddr);
>> +
>> + DRM_ERROR("failed to allocate %#x byte dma buffer, %d",
>> + obj->size, ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
>> +{
>> + struct drm_gem_object *obj = &rk_obj->base;
>> + struct drm_device *drm = obj->dev;
>> +
>> + dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
>> + &rk_obj->dma_attrs);
>> +}
>> +
>> +/* drm driver mmap file operations */
>> +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
>> +{
>> + struct drm_file *priv = filp->private_data;
>> + struct drm_device *dev = priv->minor->dev;
>> + struct drm_gem_object *obj;
>> + struct drm_vma_offset_node *node;
>> + int ret;
>> +
>> + if (drm_device_is_unplugged(dev))
>> + return -ENODEV;
>> +
>> + mutex_lock(&dev->struct_mutex);
>> +
>> + node = drm_vma_offset_exact_lookup(dev->vma_offset_manager,
>> + vma->vm_pgoff,
>> + vma_pages(vma));
>> + if (!node) {
>> + mutex_unlock(&dev->struct_mutex);
>> + DRM_ERROR("failed to find vma node.\n");
>> + return -EINVAL;
>> + } else if (!drm_vma_node_is_allowed(node, filp)) {
>> + mutex_unlock(&dev->struct_mutex);
>> + return -EACCES;
>> + }
>> +
>> + obj = container_of(node, struct drm_gem_object, vma_node);
>> + ret = rockchip_gem_mmap(obj, vma);
>> +
>> + mutex_unlock(&dev->struct_mutex);
>> +
>> + return ret;
>> +}
>> +
>> +int rockchip_drm_gem_mmap_buffer(struct file *filp,
>> + struct vm_area_struct *vma)
>> +{
>> + struct drm_gem_object *obj = filp->private_data;
>> +
>> + return rockchip_gem_mmap(obj, vma);
>> +}
>> +
>> +static const struct file_operations rockchip_drm_gem_fops = {
>> + .mmap = rockchip_drm_gem_mmap_buffer,
>> +};
>> +
>> +struct rockchip_gem_object *
>> + rockchip_gem_create_object(struct drm_device *drm, unsigned int size)
>> +{
>> + struct rockchip_gem_object *rk_obj;
>> + struct drm_gem_object *obj;
>> + struct file *filp;
>> + int ret;
>> +
>> + size = round_up(size, PAGE_SIZE);
>> +
>> + rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
>> + if (!rk_obj)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + obj = &rk_obj->base;
>> +
>> + drm_gem_private_object_init(drm, obj, size);
>> +
>> + filp = anon_inode_getfile("rockchip_gem", &rockchip_drm_gem_fops,
>> + obj, 0);
>> + if (IS_ERR(filp)) {
>> + DRM_ERROR("failed to create anon file object.\n");
>> + ret = PTR_ERR(filp);
>> + goto err_free_rk_obj;
>> + }
>> + filp->f_mode = FMODE_READ | FMODE_WRITE;
>> + obj->filp = filp;
>> +
>> + ret = drm_gem_create_mmap_offset(obj);
>> + if (ret)
>> + goto err_free_obj;
>> +
>> + ret = rockchip_gem_alloc_buf(rk_obj);
>> + if (ret)
>> + goto err_free_mmap_offset;
>> +
>> + return rk_obj;
>> +
>> +err_free_mmap_offset:
>> + drm_gem_free_mmap_offset(obj);
>> +err_free_obj:
>> + drm_gem_object_release(obj);
>> +err_free_rk_obj:
>> + kfree(rk_obj);
>> + return ERR_PTR(ret);
>> +}
>> +
>> +/*
>> + * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback
>> + * function
>> + */
>> +void rockchip_gem_free_object(struct drm_gem_object *obj)
>> +{
>> + struct rockchip_gem_object *rk_obj;
>> +
>> + drm_gem_free_mmap_offset(obj);
>> +
>> + rk_obj = to_rockchip_obj(obj);
>> +
>> + rockchip_gem_free_buf(rk_obj);
>> + drm_gem_free_mmap_offset(obj);
>> +
>> + drm_gem_object_release(obj);
>> +
>> + kfree(rk_obj);
>> +}
>> +
>> +int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
>> +{
>> + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
>> + struct drm_device *drm = obj->dev;
>> + unsigned long vm_size;
>> +
>> + vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
>> + vm_size = vma->vm_end - vma->vm_start;
>> +
>> + if (vm_size > obj->size)
>> + return -EINVAL;
>> +
>> + return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
>> + obj->size, &rk_obj->dma_attrs);
>> +}
>> +
>> +/*
>> + * rockchip_gem_create_with_handle - allocate an object with the given
>> + * size and create a gem handle on it
>> + *
>> + * returns a struct rockchip_gem_object* on success or ERR_PTR values
>> + * on failure.
>> + */
>> +static struct rockchip_gem_object *
>> +rockchip_gem_create_with_handle(struct drm_file *file_priv,
>> + struct drm_device *drm, unsigned int size,
>> + unsigned int *handle)
>> +{
>> + struct rockchip_gem_object *rk_obj;
>> + struct drm_gem_object *obj;
>> + int ret;
>> +
>> + rk_obj = rockchip_gem_create_object(drm, size);
>> + if (IS_ERR(rk_obj))
>> + return NULL;
>> +
>> + obj = &rk_obj->base;
>> +
>> + /*
>> + * allocate a id of idr table where the obj is registered
>> + * and handle has the id what user can see.
>> + */
>> + ret = drm_gem_handle_create(file_priv, obj, handle);
>> + if (ret)
>> + goto err_handle_create;
>> +
>> + /* drop reference from allocate - handle holds it now. */
>> + drm_gem_object_unreference_unlocked(obj);
>> +
>> + return rk_obj;
>> +
>> +err_handle_create:
>> + rockchip_gem_free_object(obj);
>> +
>> + return ERR_PTR(ret);
>> +}
>> +
>> +int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
>> + struct drm_device *dev, uint32_t handle,
>> + uint64_t *offset)
>> +{
>> + struct drm_gem_object *obj;
>> + int ret = 0;
>> +
>> + mutex_lock(&dev->struct_mutex);
>> +
>> + /*
>> + * get offset of memory allocated for drm framebuffer.
>> + * - this callback would be called by user application
>> + * with DRM_IOCTL_MODE_MAP_DUMB command.
>> + */
>> +
>> + obj = drm_gem_object_lookup(dev, file_priv, handle);
>> + if (!obj) {
>> + DRM_ERROR("failed to lookup gem object.\n");
>> + ret = -EINVAL;
>> + goto unlock;
>> + }
>> +
>> + ret = drm_gem_create_mmap_offset(obj);
>> + if (ret)
>> + goto out;
>> +
>> + *offset = drm_vma_node_offset_addr(&obj->vma_node);
>> + DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
>> +
>> +out:
>> + drm_gem_object_unreference(obj);
>> +unlock:
>> + mutex_unlock(&dev->struct_mutex);
>> + return ret;
>> +}
>> +
>> +/*
>> + * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
>> + * function
>> + *
>> + * This aligns the pitch and size arguments to the minimum required. wrap
>> + * this into your own function if you need bigger alignment.
>> + */
>> +int rockchip_gem_dumb_create(struct drm_file *file_priv,
>> + struct drm_device *dev,
>> + struct drm_mode_create_dumb *args)
>> +{
>> + struct rockchip_gem_object *rk_obj;
>> + int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
>> +
>> + if (args->pitch < min_pitch)
>> + args->pitch = min_pitch;
>> +
>> + if (args->size < args->pitch * args->height)
>> + args->size = args->pitch * args->height;
>> +
>> + rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
>> + &args->handle);
>> +
>> + return PTR_ERR_OR_ZERO(rk_obj);
>> +}
>> +
>> +/*
>> + * Allocate a sg_table for this GEM object.
>> + * Note: Both the table's contents, and the sg_table itself must be freed by
>> + * the caller.
>> + * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
>> + */
>> +struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
>> +{
>> + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
>> + struct drm_device *drm = obj->dev;
>> + struct sg_table *sgt = NULL;
>> + int ret;
>> +
>> + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
>> + if (!sgt)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
>> + rk_obj->dma_addr, obj->size,
>> + &rk_obj->dma_attrs);
>> + if (ret) {
>> + DRM_ERROR("failed to allocate sgt, %d\n", ret);
>> + kfree(sgt);
>> + return ERR_PTR(ret);
>> + }
>> +
>> + return sgt;
>> +}
>> +
>> +void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
>> +{
>> + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
>> +
>> + return rk_obj->kvaddr;
>> +}
>> +
>> +void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
>> +{
>> + /* Nothing to do */
>> +}
>> +
>> +int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
>> + struct vm_area_struct *vma)
>> +{
>> + struct drm_device *dev = obj->dev;
>> + int ret;
>> +
>> + mutex_lock(&dev->struct_mutex);
>> + ret = drm_gem_mmap_obj(obj, obj->size, vma);
>> + mutex_unlock(&dev->struct_mutex);
>> +
>> + return ret;
>> +}
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
>> new file mode 100644
>> index 0000000..e37b921
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
>> @@ -0,0 +1,55 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef _ROCKCHIP_DRM_GEM_H
>> +#define _ROCKCHIP_DRM_GEM_H
>> +
>> +#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base)
>> +
>> +struct rockchip_gem_object {
>> + struct drm_gem_object base;
>> + unsigned int flags;
>> +
>> + void *kvaddr;
>> + dma_addr_t dma_addr;
>> + struct dma_attrs dma_attrs;
>> +};
>> +
>> +struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
>> +struct drm_gem_object *
>> +rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
>> + struct sg_table *sgt);
>> +void *rockchip_gem_prime_vmap(struct drm_gem_object *obj);
>> +void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
>> +int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
>> + struct vm_area_struct *vma);
>> +
>> +/* drm driver mmap file operations */
>> +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
>> +
>> +/* mmap a gem object to userspace. */
>> +int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
>> +
>> +struct rockchip_gem_object *
>> + rockchip_gem_create_object(struct drm_device *drm, unsigned int size);
>> +
>> +void rockchip_gem_free_object(struct drm_gem_object *obj);
>> +
>> +int rockchip_gem_dumb_create(struct drm_file *file_priv,
>> + struct drm_device *dev,
>> + struct drm_mode_create_dumb *args);
>> +int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
>> + struct drm_device *dev, uint32_t handle,
>> + uint64_t *offset);
>> +#endif /* _ROCKCHIP_DRM_GEM_H */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> new file mode 100644
>> index 0000000..24c3946
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -0,0 +1,1373 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#include <drm/drm.h>
>> +#include <drm/drmP.h>
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/component.h>
>> +
>> +#include <linux/reset.h>
>> +#include <linux/iommu.h>
>> +#include <linux/delay.h>
>> +
>> +#include <video/of_display_timing.h>
>> +#include <video/of_videomode.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_fbdev.h"
>> +#include "rockchip_drm_gem.h"
>> +#include "rockchip_drm_fb.h"
>> +#include "rockchip_drm_vop.h"
>> +
>> +#define VOP_DEFAULT_FRAMERATE 60
>> +#define VOP_MAX_WIN_SUPPORT 5
>> +#define VOP_DEFAULT_CURSOR 1
>> +#define VOP_REG(off, _mask, s) \
>> + {.offset = off, \
>> + .mask = _mask, \
>> + .shift = s,}
>> +
>> +#define __REG_SET(x, off, mask, shift, v) \
>> + vop_mask_write(x, off, (mask) << shift, (v) << shift)
>> +
>> +#define REG_SET(x, base, reg, v) \
>> + __REG_SET(x, base + reg.offset, reg.mask, reg.shift, v)
>> +
>> +#define VOP_WIN_SET(x, win, name, v) \
>> + REG_SET(x, win->base, win->phy->name, v)
>> +#define VOP_CTRL_SET(x, name, v) \
>> + REG_SET(x, 0, (x)->data->ctrl->name, v)
>> +
>> +#define VOP_WIN_GET_YRGBADDR(ctx, win) \
>> + vop_readl(ctx, win->base + win->phy->yrgb_mst.offset)
>> +
>> +#define to_vop_ctx(x) container_of(x, struct vop_context, crtc)
>> +#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
>> +
>> +struct rockchip_plane {
>> + int id;
>> + struct drm_plane base;
>> + const struct vop_win *win;
>> + struct vop_context *ctx;
> Isn't ctx just: to_vop_ctx(base->crtc)
>
OK. we can use to_vop_ctx(base->crtc) to get ctx.
>> +
>> + uint32_t pending_yrgb_mst;
>> + struct drm_framebuffer *front_fb;
>> + struct drm_framebuffer *pending_fb;
>> + bool enabled;
>> +};
>> +
>> +struct vop_context {
>> + struct device *dev;
>> + struct drm_device *drm_dev;
>> + struct drm_crtc crtc;
>> + struct drm_pending_vblank_event *event;
>> + struct vop_driver *drv;
>> + unsigned int dpms;
>> + unsigned int win_mask;
>> + wait_queue_head_t wait_vsync_queue;
>> + atomic_t wait_vsync_event;
>> +
>> + struct workqueue_struct *vsync_wq;
>> + struct work_struct vsync_work;
>> +
>> + /* mutex vsync_ work */
>> + struct mutex vsync_mutex;
>> + bool vsync_work_pending;
>> +
>> + struct vop_driver_data *data;
>> +
>> + uint32_t *regsbak;
>> + void __iomem *regs;
>> +
>> + /* physical map length of vop register */
>> + uint32_t len;
>> +
>> + /* one time only one process allowed to config the register */
>> + spinlock_t reg_lock;
>> + /* lock vop irq reg */
>> + spinlock_t irq_lock;
>> +
>> + unsigned int irq;
>> +
>> + /* vop AHP clk */
>> + struct clk *hclk;
>> + /* vop dclk */
>> + struct clk *dclk;
>> + /* vop share memory frequency */
>> + struct clk *aclk;
>> + uint32_t pixclock;
>> +
>> + int pipe;
>> + bool clk_on;
>> +};
>> +
>> +enum vop_data_format {
>> + VOP_FMT_ARGB8888 = 0,
>> + VOP_FMT_RGB888,
>> + VOP_FMT_RGB565,
>> + VOP_FMT_YUV420SP = 4,
>> + VOP_FMT_YUV422SP,
>> + VOP_FMT_YUV444SP,
>> +};
>> +
>> +struct vop_reg_data {
>> + uint32_t offset;
>> + uint32_t value;
>> +};
>> +
>> +struct vop_reg {
>> + uint32_t offset;
>> + uint32_t shift;
>> + uint32_t mask;
>> +};
>> +
>> +struct vop_ctrl {
>> + struct vop_reg standby;
>> + struct vop_reg gate_en;
>> + struct vop_reg mmu_en;
>> + struct vop_reg rgb_en;
>> + struct vop_reg edp_en;
>> + struct vop_reg hdmi_en;
>> + struct vop_reg mipi_en;
>> + struct vop_reg out_mode;
>> + struct vop_reg dither_down;
>> + struct vop_reg dither_up;
>> + struct vop_reg pin_pol;
>> +
>> + struct vop_reg htotal_pw;
>> + struct vop_reg hact_st_end;
>> + struct vop_reg vtotal_pw;
>> + struct vop_reg vact_st_end;
>> + struct vop_reg hpost_st_end;
>> + struct vop_reg vpost_st_end;
>> +};
>> +
>> +struct vop_win_phy {
>> + const uint32_t *data_formats;
>> + uint32_t nformats;
>> +
>> + struct vop_reg enable;
>> + struct vop_reg format;
>> + struct vop_reg act_info;
>> + struct vop_reg dsp_info;
>> + struct vop_reg dsp_st;
>> + struct vop_reg yrgb_mst;
>> + struct vop_reg uv_mst;
>> + struct vop_reg yrgb_vir;
>> + struct vop_reg uv_vir;
>> +
>> + struct vop_reg dst_alpha_ctl;
>> + struct vop_reg src_alpha_ctl;
>> +};
>> +
>> +struct vop_win {
>> + uint32_t base;
>> + const struct vop_win_phy *phy;
>> +};
>> +
>> +struct vop_driver_data {
>> + const void *init_table;
>> + int table_size;
>> + const struct vop_ctrl *ctrl;
>> + const struct vop_win *win[VOP_MAX_WIN_SUPPORT];
>> +};
>> +
>> +static const uint32_t formats_01[] = {
>> + DRM_FORMAT_XRGB8888,
>> + DRM_FORMAT_ARGB8888,
>> + DRM_FORMAT_RGB888,
>> + DRM_FORMAT_RGB565,
>> + DRM_FORMAT_NV12,
>> + DRM_FORMAT_NV16,
>> + DRM_FORMAT_NV24,
>> +};
>> +
>> +static const uint32_t formats_234[] = {
>> + DRM_FORMAT_XRGB8888,
>> + DRM_FORMAT_ARGB8888,
>> + DRM_FORMAT_RGB888,
>> + DRM_FORMAT_RGB565,
>> +};
>> +
>> +static const struct vop_win_phy win01_data = {
>> + .data_formats = formats_01,
>> + .nformats = ARRAY_SIZE(formats_01),
>> + .enable = VOP_REG(WIN0_CTRL0, 0x1, 0),
>> + .format = VOP_REG(WIN0_CTRL0, 0x7, 1),
>> + .act_info = VOP_REG(WIN0_ACT_INFO, 0x1fff1fff, 0),
>> + .dsp_info = VOP_REG(WIN0_DSP_INFO, 0x1fff1fff, 0),
>> + .dsp_st = VOP_REG(WIN0_DSP_ST, 0x1fff1fff, 0),
>> + .yrgb_mst = VOP_REG(WIN0_YRGB_MST, 0xffffffff, 0),
>> + .uv_mst = VOP_REG(WIN0_CBR_MST, 0xffffffff, 0),
>> + .yrgb_vir = VOP_REG(WIN0_VIR, 0x3fff, 0),
>> + .uv_vir = VOP_REG(WIN0_VIR, 0x3fff, 16),
>> + .src_alpha_ctl = VOP_REG(WIN0_SRC_ALPHA_CTRL, 0xff, 0),
>> + .dst_alpha_ctl = VOP_REG(WIN0_DST_ALPHA_CTRL, 0xff, 0),
>> +};
>> +
>> +static const struct vop_win_phy win23_data = {
>> + .data_formats = formats_234,
>> + .nformats = ARRAY_SIZE(formats_234),
>> + .enable = VOP_REG(WIN2_CTRL0, 0x1, 0),
>> + .format = VOP_REG(WIN2_CTRL0, 0x7, 1),
>> + .dsp_info = VOP_REG(WIN2_DSP_INFO0, 0x0fff0fff, 0),
>> + .dsp_st = VOP_REG(WIN2_DSP_ST0, 0x1fff1fff, 0),
>> + .yrgb_mst = VOP_REG(WIN2_MST0, 0xffffffff, 0),
>> + .yrgb_vir = VOP_REG(WIN2_VIR0_1, 0x1fff, 0),
>> + .src_alpha_ctl = VOP_REG(WIN2_SRC_ALPHA_CTRL, 0xff, 0),
>> + .dst_alpha_ctl = VOP_REG(WIN2_DST_ALPHA_CTRL, 0xff, 0),
>> +};
>> +
>> +static const struct vop_win_phy cursor_data = {
>> + .data_formats = formats_234,
>> + .nformats = ARRAY_SIZE(formats_234),
>> + .enable = VOP_REG(HWC_CTRL0, 0x1, 0),
>> + .format = VOP_REG(HWC_CTRL0, 0x7, 1),
>> + .dsp_st = VOP_REG(HWC_DSP_ST, 0x1fff1fff, 0),
>> + .yrgb_mst = VOP_REG(HWC_MST, 0xffffffff, 0),
>> +};
>> +
>> +static const struct vop_win win0 = {
>> + .base = 0,
>> + .phy = &win01_data,
>> +};
>> +
>> +static const struct vop_win win1 = {
>> + .base = 0x40,
>> + .phy = &win01_data,
>> +};
>> +
>> +static const struct vop_win win2 = {
>> + .base = 0,
>> + .phy = &win23_data,
>> +};
>> +
>> +static const struct vop_win win3 = {
>> + .base = 0x50,
>> + .phy = &win23_data,
>> +};
>> +
>> +static const struct vop_win win_cursor = {
>> + .base = 0,
>> + .phy = &cursor_data,
>> +};
>> +
>> +static const struct vop_ctrl ctrl_data = {
>> + .standby = VOP_REG(SYS_CTRL, 0x1, 22),
>> + .gate_en = VOP_REG(SYS_CTRL, 0x1, 23),
>> + .mmu_en = VOP_REG(SYS_CTRL, 0x1, 20),
>> + .rgb_en = VOP_REG(SYS_CTRL, 0x1, 12),
>> + .hdmi_en = VOP_REG(SYS_CTRL, 0x1, 13),
>> + .edp_en = VOP_REG(SYS_CTRL, 0x1, 14),
>> + .mipi_en = VOP_REG(SYS_CTRL, 0x1, 15),
>> + .dither_down = VOP_REG(DSP_CTRL1, 0xf, 1),
>> + .dither_up = VOP_REG(DSP_CTRL1, 0x1, 6),
>> + .out_mode = VOP_REG(DSP_CTRL0, 0xf, 0),
>> + .pin_pol = VOP_REG(DSP_CTRL0, 0xf, 4),
>> + .htotal_pw = VOP_REG(DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
>> + .hact_st_end = VOP_REG(DSP_HACT_ST_END, 0x1fff1fff, 0),
>> + .vtotal_pw = VOP_REG(DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>> + .vact_st_end = VOP_REG(DSP_VACT_ST_END, 0x1fff1fff, 0),
>> + .hpost_st_end = VOP_REG(POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>> + .vpost_st_end = VOP_REG(POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +};
>> +
>> +static const struct vop_reg_data vop_init_reg_table[] = {
>> + {SYS_CTRL, 0x00801000},
>> + {DSP_CTRL0, 0x00000000},
>> + {WIN0_CTRL0, 0x00000080},
>> + {WIN1_CTRL0, 0x00000080},
>> +};
>> +
>> +static const struct vop_driver_data rockchip_rk3288_vop = {
>> + .init_table = vop_init_reg_table,
>> + .table_size = ARRAY_SIZE(vop_init_reg_table),
>> + .ctrl = &ctrl_data,
>> + .win[0] = &win0,
>> + .win[1] = &win1,
>> + .win[2] = &win2,
>> + .win[3] = &win3,
>> + .win[4] = &win_cursor,
>> +};
>> +
>> +static const struct of_device_id vop_driver_dt_match[] = {
>> + { .compatible = "rockchip,rk3288-vop",
>> + .data = (void *)&rockchip_rk3288_vop },
>> + {},
>> +};
>> +
>> +static inline void vop_writel(struct vop_context *ctx,
>> + uint32_t offset, uint32_t v)
>> +{
>> + writel(v, ctx->regs + offset);
>> + ctx->regsbak[offset >> 2] = v;
>> +}
>> +
>> +static inline uint32_t vop_readl(struct vop_context *ctx, uint32_t offset)
>> +{
>> + return readl(ctx->regs + offset);
>> +}
>> +
>> +static inline void vop_cfg_done(struct vop_context *ctx)
>> +{
>> + writel(0x01, ctx->regs + REG_CFG_DONE);
>> +}
>> +
>> +static inline void vop_mask_write(struct vop_context *ctx,
>> + uint32_t offset, uint32_t mask, uint32_t v)
>> +{
>> + if (mask) {
>> + uint32_t cached_val = ctx->regsbak[offset >> 2];
>> +
>> + cached_val = (cached_val & ~mask) | v;
>> + writel(cached_val, ctx->regs + offset);
>> + ctx->regsbak[offset >> 2] = cached_val;
>> + }
>> +}
>> +
>> +static inline struct vop_driver_data *vop_get_driver_data(struct device *dev)
>> +{
>> + const struct of_device_id *of_id =
>> + of_match_device(vop_driver_dt_match, dev);
>> +
>> + return (struct vop_driver_data *)of_id->data;
>> +}
>> +
>> +static enum vop_data_format vop_convert_format(uint32_t format)
>> +{
>> + switch (format) {
>> + case DRM_FORMAT_XRGB8888:
>> + case DRM_FORMAT_ARGB8888:
>> + return VOP_FMT_ARGB8888;
>> + case DRM_FORMAT_RGB888:
>> + return VOP_FMT_RGB888;
>> + case DRM_FORMAT_RGB565:
>> + return VOP_FMT_RGB565;
>> + case DRM_FORMAT_NV12:
>> + return VOP_FMT_YUV420SP;
>> + case DRM_FORMAT_NV16:
>> + return VOP_FMT_YUV422SP;
>> + case DRM_FORMAT_NV24:
>> + return VOP_FMT_YUV444SP;
>> + default:
>> + DRM_ERROR("unsupport format[%08x]\n", format);
>> + return -EINVAL;
>> + }
>> +}
>> +
>> +static bool is_alpha_support(uint32_t format)
>> +{
>> + switch (format) {
>> + case DRM_FORMAT_ARGB8888:
>> + return true;
>> + default:
>> + return false;
>> + }
>> +}
>> +
>> +/* TODO(djkurtz): move generic 'setup slave rk_iommu' code somewhere common */
>> +int vop_iommu_init(struct vop_context *ctx)
>> +{
>> + struct device *dev = ctx->dev;
>> + struct device_node *np = dev->of_node;
>> + struct platform_device *pd;
>> + int count;
>> + int ret;
>> + struct of_phandle_args args;
>> +
>> + /* Each VOP must have exactly one iommu node, with no args */
>> + count = of_count_phandle_with_args(np, "iommus", "#iommu-cells");
>> + if (count != 1) {
>> + dev_err(dev, "of_count_phandle_with_args(%s) => %d\n",
>> + np->full_name, count);
>> + return -EINVAL;
>> + }
>> +
>> + ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0,
>> + &args);
>> + if (ret) {
>> + dev_err(dev, "of_parse_phandle_with_args(%s) => %d\n",
>> + np->full_name, ret);
>> + return ret;
>> + }
>> + if (args.args_count != 0) {
>> + dev_err(dev, "incorrect number of iommu params found for %s (found %d, expected 0)\n",
>> + args.np->full_name, args.args_count);
>> + return -EINVAL;
>> + }
>> +
>> + pd = of_find_device_by_node(args.np);
>> + of_node_put(args.np);
>> + if (!pd) {
>> + dev_err(dev, "iommu %s not found\n", args.np->full_name);
>> + return -EPROBE_DEFER;
>> + }
>> +
>> + /* TODO(djkurtz): handle multiple slave iommus for a single master */
>> + dev->archdata.iommu = &pd->dev;
>> +
>> + ret = rockchip_drm_dma_attach_device(ctx->drm_dev, dev);
>> + if (ret) {
>> + dev_err(dev, "failed to attach to drm dma mapping, %d\n", ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void vop_iommu_fini(struct vop_context *ctx)
>> +{
>> + rockchip_drm_dma_detach_device(ctx->drm_dev, ctx->dev);
>> +}
>> +
>> +static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
> This function can use some documentation to define the variables which are
> somewhat misleadingly named:
> start: starting pixel in screen coordinates
> length: length of buffer "row" in pixels
> end: one pixel past end of buffer in screen coordinates
> last: width of screen, or one pixel past end of screen
> size: number of pixels of buffer to display on screen
>
>> +{
>> + int end = start + length;
>> + int size = 0;
>> +
>> + if (start <= 0) {
>> + if (end > 0)
>> + size = min_t(unsigned, end, last);
>> + } else if (start <= last) {
> start < last
>
> /* if (start == last), we are starting 1 pixel past the end of the screen,
> so no need to compute size :-), it will be 0. */
right, got it.
>> + size = min_t(unsigned, last - start, length);
>> + }
>> +
>> + return size;
>> +}
>> +
>> +static int vop_clk_enable(struct vop_context *ctx)
>> +{
>> + int ret;
>> +
>> + if (!ctx->clk_on) {
>> + ret = clk_prepare_enable(ctx->hclk);
> I saw a code review recently that suggested to do
> "clk_prepare/_unprepare()" in probe()/remove(), and just do
> clk_enable()/clk_disable() at runtime.
so , vop_clk_enable is called by probe() and runtime,
OK, I will change "clk_prepare/_unprepare()" / to
"clk_enable()/clk_disable()"
>> + if (ret < 0) {
>> + dev_err(ctx->dev, "failed to enable hclk\n");
>> + return ret;
>> + }
>> +
>> + ret = clk_prepare_enable(ctx->dclk);
>> + if (ret < 0) {
>> + dev_err(ctx->dev, "failed to enable dclk\n");
>> + goto err_dclk;
>> + }
>> +
>> + ret = clk_prepare_enable(ctx->aclk);
>> + if (ret < 0) {
>> + dev_err(ctx->dev, "failed to enable aclk\n");
>> + goto err_aclk;
>> + }
>> + ctx->clk_on = true;
>> + }
>> +
>> + return ret;
>> +err_aclk:
>> + clk_disable_unprepare(ctx->aclk);
> this should be dclk
Oh, yes, mistake...
>
>> +err_dclk:
>> + clk_disable_unprepare(ctx->hclk);
>> + return ret;
>> +}
>> +
>> +static void vop_clk_disable(struct vop_context *ctx)
>> +{
>> + if (ctx->clk_on) {
>> + clk_disable_unprepare(ctx->dclk);
>> + clk_disable_unprepare(ctx->hclk);
>> + clk_disable_unprepare(ctx->aclk);
> disable in inverse order from enable:
>
> aclk
> dclk
> hclk
OK.
>> + ctx->clk_on = false;
>> + }
>> +}
>> +
>> +static void vop_power_on(struct vop_context *ctx)
>> +{
>> + if (vop_clk_enable(ctx) < 0) {
>> + dev_err(ctx->dev, "failed to enable clks\n");
>> + return;
>> + }
>> +
>> + spin_lock(&ctx->reg_lock);
>> +
>> + VOP_CTRL_SET(ctx, standby, 0);
>> +
>> + spin_unlock(&ctx->reg_lock);
>> +}
>> +
>> +static void vop_power_off(struct vop_context *ctx)
>> +{
>> + spin_lock(&ctx->reg_lock);
>> +
>> + VOP_CTRL_SET(ctx, standby, 1);
>> +
>> + spin_unlock(&ctx->reg_lock);
>> +
>> + vop_clk_disable(ctx);
>> +}
>> +
>> +static int rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>> + struct drm_framebuffer *fb, int crtc_x,
>> + int crtc_y, unsigned int crtc_w,
>> + unsigned int crtc_h, uint32_t src_x,
>> + uint32_t src_y, uint32_t src_w, uint32_t src_h)
>> +{
>> + struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
>> + const struct vop_win *win = rockchip_plane->win;
>> + struct vop_context *ctx = to_vop_ctx(crtc);
>> + struct drm_gem_object *obj;
>> + struct rockchip_gem_object *rk_obj;
>> + unsigned long offset;
>> + unsigned int actual_w;
>> + unsigned int actual_h;
>> + unsigned int dsp_stx;
>> + unsigned int dsp_sty;
>> + unsigned int y_vir_stride;
>> + dma_addr_t yrgb_mst;
>> + enum vop_data_format format;
>> + uint32_t val;
>> + bool is_alpha;
>> +
>> + if (!win) {
>> + DRM_ERROR("can't find win data for vop, failed\n");
>> + return -EINVAL;
>> + }
> There should be no need for this error checking.
right, rockchip_plane always has a vop_win, no need check here, I will
remove the check.
>> +
>> + obj = rockchip_fb_get_gem_obj(fb, 0);
>> + if (!obj) {
>> + DRM_ERROR("fail to get rockchip gem object from framebuffer\n");
>> + return -EINVAL;
>> + }
> Nor this.
>
>> +
>> + rk_obj = to_rockchip_obj(obj);
>> +
>> + yrgb_mst = rk_obj->dma_addr;
>> + if (yrgb_mst <= 0)
>> + return -ENOMEM;
> Nor this:
> I don't think dma_addr_t can be less than 0.
> Also, dma_addr_t == 0 could be a valid iova.
right.
> src_* are actually 16.16 fixed point 'subpixel' units, but crtc_x is in pixels.
>
> However, since you aren't implementing sub-pixel positioning, you can do:
>
> if (src_x & 0xffff || src_y & 0xffff || src_w & 0xffff || src_h & 0xffff)
> return -EINVAL;
>
> And, since you aren't implementing plane scaling:
>
> src_w >>= 16;
> src_h >>= 16;
> src_x >>= 16;
> src_y >>= 16;
>
> if (crtc_w != src_w || crtc_h != src_h)
> return -EINVAL;
OK, got it.
>> +
>> + actual_w = rockchip_plane_get_size(crtc_x,
>> + crtc_w, crtc->mode.hdisplay);
>> + actual_h = rockchip_plane_get_size(crtc_y,
>> + crtc_h, crtc->mode.vdisplay);
> crtc_w and crtc_h seem to imply the width & height of the crtc, but
> really they are they are
> desired width and height of the framebuffer in crtc pixel units.
>
> actual_w and actual_h are the number of pixels of this framebuffer that will
> actually be shown on the crtc. What happens if one of them is 0? Perhaps we
> should just return -EINVAL. Otherwise you might compute -1 below when setting
> act_info/dsp_info, and end up programming a very large number.
correct, got it.
>> + if (crtc_x < 0) {
>> + if (actual_w)
>> + src_x -= crtc_x;
>> + crtc_x = 0;
>> + }
>> +
>> + if (crtc_y < 0) {
>> + if (actual_h)
>> + src_y -= crtc_y;
>> + crtc_y = 0;
>> + }
>> +
>> + dsp_stx = crtc_x + crtc->mode.htotal - crtc->mode.hsync_start;
>> + dsp_sty = crtc_y + crtc->mode.vtotal - crtc->mode.vsync_start;
> The additions herer are because you have the vblank at the beginning
> of the frame.
> Can you simplify this by moving vblank to the end of the frame? (see below)
>
>> +
>> + offset = src_x * (fb->bits_per_pixel >> 3);
>> + offset += src_y * fb->pitches[0];
>> +
>> + y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3);
>> + is_alpha = is_alpha_support(fb->pixel_format);
>> + format = vop_convert_format(fb->pixel_format);
> This function would return < 0 if the format not supported. Check this earlier.
> Although, it should not be possible to get an invalid format, since it is
> already verified by drm core.
OK. got it.
>> +
>> + spin_lock(&ctx->reg_lock);
>> +
>> + VOP_WIN_SET(ctx, win, format, format);
>> + VOP_WIN_SET(ctx, win, yrgb_vir, y_vir_stride);
>> + yrgb_mst += offset;
>> + VOP_WIN_SET(ctx, win, yrgb_mst, yrgb_mst);
>> + VOP_WIN_SET(ctx, win, act_info,
>> + ((actual_h - 1) << 16) | (actual_w - 1));
> Nit: You should probably be masking the individual h/w fields to 13-bits each
> before or'ing them together.
> Same with dsp_st.
OK, got it.
>> + VOP_WIN_SET(ctx, win, dsp_info,
>> + ((actual_h - 1) << 16) | (actual_w - 1));
>> + VOP_WIN_SET(ctx, win, dsp_st, (dsp_sty << 16) | dsp_stx);
>> + if (is_alpha) {
>> + VOP_WIN_SET(ctx, win, dst_alpha_ctl,
>> + DST_FACTOR_M0(ALPHA_SRC_INVERSE));
>> + val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
>> + SRC_ALPHA_M0(ALPHA_STRAIGHT) |
>> + SRC_BLEND_M0(ALPHA_PER_PIX) |
>> + SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
>> + SRC_FACTOR_M0(ALPHA_ONE);
>> + VOP_WIN_SET(ctx, win, src_alpha_ctl, val);
>> + } else {
>> + VOP_WIN_SET(ctx, win, src_alpha_ctl, SRC_ALPHA_EN(0));
>> + }
>> +
>> + VOP_WIN_SET(ctx, win, enable, 1);
>> +
>> + spin_unlock(&ctx->reg_lock);
>> +
>> + mutex_lock(&ctx->vsync_mutex);
>> +
>> + /*
>> + * Because the buffer set to vop take effect at frame start time,
>> + * we need make sure old buffer is not in use before we release
>> + * it.
>> + * reference the framebuffer, and unference it when it swap out of vop.
>> + */
>> + if (fb != rockchip_plane->front_fb) {
>> + drm_framebuffer_reference(fb);
>> + rockchip_plane->pending_fb = fb;
>> + rockchip_plane->pending_yrgb_mst = yrgb_mst;
>> + ctx->vsync_work_pending = true;
>> + }
>> + rockchip_plane->enabled = true;
>> +
>> + mutex_unlock(&ctx->vsync_mutex);
>> +
>> + spin_lock(&ctx->reg_lock);
>> + vop_cfg_done(ctx);
>> + spin_unlock(&ctx->reg_lock);
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_disable_plane(struct drm_plane *plane)
>> +{
>> + struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
>> + struct vop_context *ctx = rockchip_plane->ctx;
>> + const struct vop_win *win = rockchip_plane->win;
>> +
>> + spin_lock(&ctx->reg_lock);
>> +
>> + VOP_WIN_SET(ctx, win, enable, 0);
>> + vop_cfg_done(ctx);
>> +
>> + spin_unlock(&ctx->reg_lock);
>> +
>> + mutex_lock(&ctx->vsync_mutex);
>> +
>> + /*
>> + * clear the pending framebuffer and set vsync_work_pending true,
>> + * so that the framebuffer will unref at the next vblank.
>> + */
>> + if (rockchip_plane->pending_fb) {
>> + drm_framebuffer_unreference(rockchip_plane->pending_fb);
>> + rockchip_plane->pending_fb = NULL;
>> + }
>> +
>> + rockchip_plane->enabled = false;
>> + ctx->vsync_work_pending = true;
>> +
>> + mutex_unlock(&ctx->vsync_mutex);
>> +
>> + return 0;
>> +}
>> +
>> +static void rockchip_plane_destroy(struct drm_plane *plane)
>> +{
>> + struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
>> + struct vop_context *ctx = rockchip_plane->ctx;
>> +
>> + rockchip_disable_plane(plane);
>> + drm_plane_cleanup(plane);
>> + ctx->win_mask &= ~(1 << rockchip_plane->id);
>> + kfree(rockchip_plane);
>> +}
>> +
>> +static const struct drm_plane_funcs rockchip_plane_funcs = {
>> + .update_plane = rockchip_update_plane,
>> + .disable_plane = rockchip_disable_plane,
>> + .destroy = rockchip_plane_destroy,
>> +};
>> +
>> +struct drm_plane *rockchip_plane_init(struct vop_context *ctx,
>> + unsigned long possible_crtcs,
>> + enum drm_plane_type type)
>> +{
>> + struct rockchip_plane *rockchip_plane;
>> + struct vop_driver_data *vop_data = ctx->data;
>> + const struct vop_win *win;
>> + int i;
>> + int err;
>> +
>> + rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
>> + if (!rockchip_plane)
>> + return NULL;
> Let's propagate errors from this function, like this...
>
> return ERR_PTR(-ENOMEM);
OK.
>> +
>> + for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++) {
>> + if (!(ctx->win_mask & (1 << i))) {
>> + win = vop_data->win[i];
>> + break;
>> + }
>> + }
> The caller already knows for which window index they are init'ing the plane.
> Just have the caller pass it in.
OK.
>> +
>> + if (VOP_MAX_WIN_SUPPORT == i) {
>> + DRM_ERROR("failed to find win\n");
>> + kfree(rockchip_plane);
>> + return NULL;
>> + }
>> +
>> + ctx->win_mask |= (1 << i);
>> + rockchip_plane->id = i;
>> + rockchip_plane->win = win;
>> + rockchip_plane->ctx = ctx;
>> +
>> + err = drm_universal_plane_init(ctx->drm_dev, &rockchip_plane->base,
>> + possible_crtcs, &rockchip_plane_funcs,
>> + win->phy->data_formats,
>> + win->phy->nformats, type);
>> + if (err) {
>> + DRM_ERROR("failed to initialize plane\n");
>> + kfree(rockchip_plane);
>> + return NULL;
> return ERR_PTR(err);
OK.
>> + }
>> +
>> + return &rockchip_plane->base;
>> +}
>> +
>> +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
>> +{
>> + struct vop_context *ctx = to_vop_ctx(rockchip_find_crtc(dev, pipe));
>> + unsigned long flags;
>> +
>> + if (ctx->dpms != DRM_MODE_DPMS_ON)
>> + return -EPERM;
>> +
>> + spin_lock_irqsave(&ctx->irq_lock, flags);
>> +
>> + vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
>> + LINE_FLAG_INTR_EN(1));
>> +
>> + spin_unlock_irqrestore(&ctx->irq_lock, flags);
>> +
>> + return 0;
>> +}
>> +
>> +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
>> +{
>> + struct vop_context *ctx = to_vop_ctx(rockchip_find_crtc(dev, pipe));
>> + unsigned long flags;
>> +
>> + if (ctx->dpms != DRM_MODE_DPMS_ON)
>> + return;
>> + spin_lock_irqsave(&ctx->irq_lock, flags);
>> + vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
>> + LINE_FLAG_INTR_EN(0));
>> + spin_unlock_irqrestore(&ctx->irq_lock, flags);
>> +}
>> +
>> +static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
>> +{
>> + struct vop_context *ctx = to_vop_ctx(crtc);
>> +
>> + DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
>> +
>> + if (ctx->dpms == mode) {
>> + DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
>> + return;
>> + }
>> + if (mode > DRM_MODE_DPMS_ON) {
>> + /* wait for the completion of page flip. */
>> + if (!wait_event_timeout(ctx->wait_vsync_queue,
>> + !atomic_read(&ctx->wait_vsync_event),
>> + HZ/20))
>> + DRM_DEBUG_KMS("vblank wait timed out.\n");
>> + drm_vblank_off(crtc->dev, ctx->pipe);
>> + }
>> +
>> + switch (mode) {
>> + case DRM_MODE_DPMS_ON:
>> + vop_power_on(ctx);
>> + break;
>> + case DRM_MODE_DPMS_STANDBY:
>> + case DRM_MODE_DPMS_SUSPEND:
>> + case DRM_MODE_DPMS_OFF:
>> + vop_power_off(ctx);
>> + break;
>> + default:
>> + DRM_DEBUG_KMS("unspecified mode %d\n", mode);
>> + break;
>> + }
>> +
>> + ctx->dpms = mode;
>> +}
>> +
>> +static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
>> +{
>> + rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
>> +}
>> +
>> +static bool rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
>> + const struct drm_display_mode *mode,
>> + struct drm_display_mode *adjusted_mode)
>> +{
>> + /* just do dummy now */
>> +
>> + return true;
>> +}
>> +
>> +static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
>> + struct drm_framebuffer *old_fb);
>> +
>> +static int rockchip_drm_crtc_mode_set(struct drm_crtc *crtc,
>> + struct drm_display_mode *mode,
>> + struct drm_display_mode *adjusted_mode,
>> + int x, int y,
>> + struct drm_framebuffer *fb)
>> +{
>> + struct vop_context *ctx = to_vop_ctx(crtc);
>> + u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
>> + u16 left_margin = adjusted_mode->htotal - adjusted_mode->hsync_end;
>> + u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
>> + u16 upper_margin = adjusted_mode->vtotal - adjusted_mode->vsync_end;
> This moves the vblank/hblank to the front of the frame rather than the end.
> Is that necessary on this hardware?
> If you leave the active area origin at (0,0), it makes it a little bit simpler
> to locate overlays.
in fact, the hardware define is different with drm_display_mode define:
drm_display_mode define that:
Active Front Sync Back
Region Porch Porch
<-----------------------><----------------><-------------><-------------->
//////////////////////|
////////////////////// |
////////////////////// |.................. ................
_______________
<----- [hv]display ----->
<------------- [hv]sync_start ------------>
<--------------------- [hv]sync_end --------------------->
<-------------------------------- [hv]total ----------------------------->
but vop hardware define is that:
Sync Back Active Front
Porch Region Porch
<-----------><-------------><--------------------><-------------->
|//////////////////////|
|//////////////////////|
..............|//////////////////////|...............
|____________|
<-dsp_hs_end->
<------ dsp_hact_st------->
<----------------dsp_hact_end-------------------->
<-------------------------------- dsp_hact_end-------------------->
So, we need to convert the setting.
>> + u16 hdisplay = adjusted_mode->hdisplay;
>> + u16 vdisplay = adjusted_mode->vdisplay;
>> + u16 htotal = adjusted_mode->htotal;
>> + u16 vtotal = adjusted_mode->vtotal;
>> + struct rockchip_display_mode *priv_mode =
>> + (void *)adjusted_mode->private;
>> + unsigned long flags;
>> + int ret;
>> + uint32_t val;
>> +
>> + /* nothing to do if we haven't set the mode yet */
>> + if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
>> + return -EINVAL;
> How is it possible to be given a mode with htotal or vtotal == 0?
> How could we be doing a mode set without a mode to set?
> Can you do these verification checks to mode_fixup() instead?
OK, I will move it to mode_fixup().
>> +
>> + if (!priv_mode) {
>> + DRM_ERROR("fail to found display output type[%d]\n",
>> + priv_mode->out_type);
>> + return -EINVAL;
>> + }
>> +
>> + ret = rockchip_drm_crtc_mode_set_base(crtc, x, y, fb);
>> + if (ret)
>> + return ret;
>> +
>> + switch (priv_mode->out_type) {
>> + case ROCKCHIP_DISPLAY_TYPE_RGB:
>> + case ROCKCHIP_DISPLAY_TYPE_LVDS:
>> + VOP_CTRL_SET(ctx, rgb_en, 1);
>> + VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_P888);
>> + break;
>> + case ROCKCHIP_DISPLAY_TYPE_EDP:
>> + VOP_CTRL_SET(ctx, edp_en, 1);
>> + VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_AAAA);
>> + break;
>> + case ROCKCHIP_DISPLAY_TYPE_HDMI:
>> + VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_AAAA);
>> + VOP_CTRL_SET(ctx, hdmi_en, 1);
>> + break;
>> + default:
>> + DRM_ERROR("unsupport out type[%d]\n", priv_mode->out_type);
>> + return -EINVAL;
>> + };
> This switch is directing the vop output to a particular output (edp,
> lvds, hdmi, etc)
> based on a private field added to the drm_display_mode by the down-pipe encoder.
> However, on other hardware, it is possible to drive multiple outputs
> from the same crtc.
> This is a useful feature, for mirroring one framebuffer to multiple displays.
>
> Is that possible for rk3288 too?
> It looks like it should be possible to set multiple rgb_en, edp_en, hdmi_en, etc
> bits at the same time.
>
> If so, then we can't really make the encoder selection part of the mode.
> Instead, what we can try to do is implement this output selection step by
> providing a helper function from the vop for the encoders to call in their own
> mode_set() callback.
>
> Also, vop can expose another helper for allowing the encoder to select the vop
> pixel output format that it requires (ie, LVDS panel encoder can select 565).
OK, I would provide a helper function for encoders to call instead of
using drm_display_mode private.
>> +
>> + val = 0x8;
>> + val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0;
>> + val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? (1 << 1) : 0;
>> + VOP_CTRL_SET(ctx, pin_pol, val);
>> +
>> + VOP_CTRL_SET(ctx, htotal_pw, (htotal << 16) | hsync_len);
>> + val = (hsync_len + left_margin) << 16;
>> + val |= hsync_len + left_margin + hdisplay;
>> + VOP_CTRL_SET(ctx, hact_st_end, val);
>> + VOP_CTRL_SET(ctx, hpost_st_end, val);
>> +
>> + VOP_CTRL_SET(ctx, vtotal_pw, (vtotal << 16) | vsync_len);
>> + val = (vsync_len + upper_margin) << 16;
>> + val |= vsync_len + upper_margin + vdisplay;
>> + VOP_CTRL_SET(ctx, vact_st_end, val);
>> + VOP_CTRL_SET(ctx, vpost_st_end, val);
>> +
>> + spin_lock_irqsave(&ctx->irq_lock, flags);
>> +
>> + vop_mask_write(ctx, INTR_CTRL0, DSP_LINE_NUM_MASK,
>> + DSP_LINE_NUM(vsync_len + upper_margin + vdisplay));
>> +
>> + spin_unlock_irqrestore(&ctx->irq_lock, flags);
>> +
>> + clk_set_rate(ctx->dclk, adjusted_mode->clock * 1000);
> When we are changing modes, can we just change the dclk like this while a
> previous frame is already being scanned out using the old clock?
> Shouldn't we synchronize changing clocks:
>
> - schedule any current scanout to stop at next vblank
> - wait for that vblank
> - disable clock
> - program new mode
> - program new framebuffer(s) to match that new mode.
> - start clock
>
> Or maybe like what you do in initial before turning on the iommu:
>
> - disable dclk
> - do a dclk & ahb reset
> - program new mode
> - program new fb(s)
> - enable dclk.
right, and there is another problem:
all mode config will take effect at frame start, and at runtime, frame
start event maybe happen at any place
when setting mode config:
VOP_CTRL_SET(ctx, htotal_pw, (htotal << 16) | hsync_len);
----> frame start
VOP_CTRL_SET(ctx, hact_st_end, val);
then htotal_pw take effect, but hact_st_end should wait for next
frame start, so we got a wrong frame.
so I want to change it like your advices :
mode_set:
- disable dclk
- program new fb(s)
- program new mode
- do a dclk reset, reset the frame scan point to the beginning, then all
setting will take effect.
- dclk set rate.
- enable dclk
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
>> + struct drm_framebuffer *old_fb)
>> +{
>> + unsigned int crtc_w;
>> + unsigned int crtc_h;
>> + int ret;
>> +
>> + crtc_w = crtc->primary->fb->width - crtc->x;
>> + crtc_h = crtc->primary->fb->height - crtc->y;
>> +
>> + ret = rockchip_update_plane(crtc->primary, crtc, crtc->primary->fb, 0,
>> + 0, crtc_w, crtc_h, crtc->x, crtc->y, crtc_w,
>> + crtc_h);
>
> rockchip_update_plane expects its src_x, src_y, src_h, src_w to be in 16.16
> 'sub-pixel' units, so you need to << 16 the last 4 parameters here.
>
> It might be more convenient to split rockchip_update_plane() into two functions:
> (1) rockchip_update_plane() which takes 16.16 src_ and is used for the
> drm_plane_funcs.update_plane. This function converts src_* from 16.16 to
> pixels, and then calls:
> (2) a helper function which just takes src_ in whole pixel units. The helper
> function you can call from rockchip_drm_crtc_mode_set_base() and
> rockchip_drm_crtc_page_flip(). In fact, since we do not support scaling,
> the helper probably does not even need src_w and src_h arguments.
OK, Got it.
>> + if (ret < 0) {
>> + DRM_ERROR("fail to update plane\n");
>> + return -EINVAL;
> return ret;
OK.
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
>> +{
>> + /* just do dummy now */
>> +}
>> +
>> +static const struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
>> + .dpms = rockchip_drm_crtc_dpms,
>> + .prepare = rockchip_drm_crtc_prepare,
>> + .mode_fixup = rockchip_drm_crtc_mode_fixup,
>> + .mode_set = rockchip_drm_crtc_mode_set,
>> + .mode_set_base = rockchip_drm_crtc_mode_set_base,
>> + .commit = rockchip_drm_crtc_commit,
>> +};
>> +
>> +static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
>> + struct drm_framebuffer *fb,
>> + struct drm_pending_vblank_event *event,
>> + uint32_t page_flip_flags)
>> +{
>> + struct drm_device *dev = crtc->dev;
>> + struct vop_context *ctx = to_vop_ctx(crtc);
>> + struct drm_framebuffer *old_fb = crtc->primary->fb;
>> + unsigned int crtc_w;
>> + unsigned int crtc_h;
>> + int ret;
>> +
>> + /* when the page flip is requested, crtc's dpms should be on */
>> + if (ctx->dpms > DRM_MODE_DPMS_ON) {
>> + DRM_DEBUG("failed page flip request at dpms[%d].\n", ctx->dpms);
>> + return 0;
>> + }
>> +
>> + ret = drm_vblank_get(dev, ctx->pipe);
>> + if (ret) {
>> + DRM_DEBUG("failed to acquire vblank counter\n");
>> + return ret;
>> + }
>> +
>> + spin_lock_irq(&dev->event_lock);
>> + if (ctx->event) {
>> + spin_unlock_irq(&dev->event_lock);
>> + DRM_ERROR("already pending flip!\n");
>> + return -EBUSY;
>> + }
>> + ctx->event = event;
>> + atomic_set(&ctx->wait_vsync_event, 1);
>> + spin_unlock_irq(&dev->event_lock);
>> +
>> + crtc->primary->fb = fb;
>> + crtc_w = crtc->primary->fb->width - crtc->x;
>> + crtc_h = crtc->primary->fb->height - crtc->y;
>> +
>> + ret = rockchip_update_plane(crtc->primary, crtc, fb, 0, 0, crtc_w,
>> + crtc_h, crtc->x, crtc->y, crtc_w, crtc_h);
>> + if (ret) {
>> + crtc->primary->fb = old_fb;
>> +
>> + spin_lock_irq(&dev->event_lock);
>> + drm_vblank_put(dev, ctx->pipe);
>> + atomic_set(&ctx->wait_vsync_event, 0);
>> + ctx->event = NULL;
>> + spin_unlock_irq(&dev->event_lock);
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
>> +{
>> + struct rockchip_drm_private *dev_priv = dev->dev_private;
>> + struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
>> + struct vop_context *ctx;
>> + unsigned long flags;
>> +
>> + if (!drm_crtc)
>> + return;
> Can you add a comment describing when this can happen?
nothing would happen, I think drm_crtc would not be NULL, I will just
remove it.
>> +
>> + ctx = to_vop_ctx(drm_crtc);
>> +
>> + spin_lock_irqsave(&dev->event_lock, flags);
>> +
>> + if (ctx->event) {
>> + drm_send_vblank_event(dev, -1, ctx->event);
>> + drm_vblank_put(dev, pipe);
>> + atomic_set(&ctx->wait_vsync_event, 0);
>> + wake_up(&ctx->wait_vsync_queue);
>> + ctx->event = NULL;
>> + }
>> +
>> + spin_unlock_irqrestore(&dev->event_lock, flags);
>> +}
>> +
>> +void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < dev->num_crtcs; i++)
>> + rockchip_drm_crtc_finish_pageflip(dev, i);
>> +}
>> +
>> +static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
>> +{
>> + struct vop_context *ctx = to_vop_ctx(crtc);
>> + struct rockchip_drm_private *private = crtc->dev->dev_private;
>> +
>> + private->crtc[ctx->pipe] = NULL;
>> + drm_crtc_cleanup(crtc);
>> +}
>> +
>> +static const struct drm_crtc_funcs rockchip_crtc_funcs = {
>> + .set_config = drm_crtc_helper_set_config,
>> + .page_flip = rockchip_drm_crtc_page_flip,
>> + .destroy = rockchip_drm_crtc_destroy,
>> +};
>> +
>> +static void rockchip_vsync_worker(struct work_struct *work)
>> +{
>> + struct vop_context *ctx = container_of(work, struct vop_context,
>> + vsync_work);
>> + struct drm_device *drm = ctx->drm_dev;
>> + struct rockchip_drm_private *dev_priv = drm->dev_private;
>> + struct drm_crtc *crtc = dev_priv->crtc[ctx->pipe];
>> + struct rockchip_plane *rockchip_plane;
>> + struct drm_plane *plane;
>> + uint32_t yrgb_mst;
>> +
>> + mutex_lock(&ctx->vsync_mutex);
>> +
>> + ctx->vsync_work_pending = false;
>> +
>> + list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
>> + rockchip_plane = to_rockchip_plane(plane);
>> +
>> + if (rockchip_plane->ctx != ctx)
>> + continue;
>> + if (rockchip_plane->enabled && !rockchip_plane->pending_fb)
>> + continue;
>> + if (!rockchip_plane->enabled && !rockchip_plane->front_fb)
>> + continue;
>> + /*
>> + * make sure the yrgb_mst take effect, so that
>> + * we can unreference the old framebuffer.
>> + */
>> + yrgb_mst = VOP_WIN_GET_YRGBADDR(ctx, rockchip_plane->win);
>> + if (rockchip_plane->pending_yrgb_mst != yrgb_mst) {
>> + /*
>> + * some plane no complete, unref at next vblank
>> + */
>> + ctx->vsync_work_pending = true;
>> + continue;
>> + }
>> +
>> + /*
>> + * drm_framebuffer_unreference maybe call iommu unmap,
>> + * and iommu not allow unmap buffer at irq context,
>> + * so we do drm_framebuffer_unreference at queue_work.
>> + */
>> + if (rockchip_plane->front_fb)
>> + drm_framebuffer_unreference(rockchip_plane->front_fb);
>> +
>> + rockchip_plane->front_fb = rockchip_plane->pending_fb;
>> + rockchip_plane->pending_fb = NULL;
>> +
>> + /*
>> + * if primary plane flip complete, sending the event to
>> + * userspace
>> + */
>> + if (&rockchip_plane->base == crtc->primary)
>> + rockchip_drm_crtc_finish_pageflip(ctx->drm_dev,
>> + ctx->pipe);
>> + }
>> +
>> + mutex_unlock(&ctx->vsync_mutex);
>> +}
>> +
>> +static irqreturn_t rockchip_vop_isr(int irq, void *data)
>> +{
>> + struct vop_context *ctx = data;
>> + uint32_t intr0_reg;
>> + unsigned long flags;
>> +
>> + intr0_reg = vop_readl(ctx, INTR_CTRL0);
>> + if (intr0_reg & LINE_FLAG_INTR) {
>> + spin_lock_irqsave(&ctx->irq_lock, flags);
>> + vop_writel(ctx, INTR_CTRL0, intr0_reg | LINE_FLAG_INTR_CLR);
>> + spin_unlock_irqrestore(&ctx->irq_lock, flags);
>> + } else {
>> + return IRQ_NONE;
>> + }
>> +
>> + drm_handle_vblank(ctx->drm_dev, ctx->pipe);
>> + if (ctx->vsync_work_pending)
>> + queue_work(ctx->vsync_wq, &ctx->vsync_work);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int vop_create_crtc(struct vop_context *ctx)
>> +{
>> + struct device *dev = ctx->dev;
>> + struct drm_device *drm_dev = ctx->drm_dev;
>> + struct drm_plane *primary, *cursor;
>> + unsigned long possible_crtcs;
>> + struct drm_crtc *crtc;
>> + int ret;
>> + int nr;
>> +
>> + ctx->win_mask = 0;
>> + crtc = &ctx->crtc;
>> +
>> + ret = rockchip_drm_add_crtc(drm_dev, crtc, dev->of_node);
>> + if (ret < 0)
>> + return ret;
>> + ctx->pipe = ret;
>> +
>> + possible_crtcs = (1 << ctx->pipe);
>> +
>> + primary = rockchip_plane_init(ctx, possible_crtcs,
>> + DRM_PLANE_TYPE_PRIMARY);
>> + if (!primary) {
>> + DRM_ERROR("fail to init primary plane\n");
>> + return -EINVAL;
>> + }
>> +
>> + for (nr = 1; nr < ROCKCHIP_MAX_PLANE; nr++) {
>> + if (nr == VOP_DEFAULT_CURSOR) {
>> + cursor = rockchip_plane_init(ctx, possible_crtcs,
>> + DRM_PLANE_TYPE_CURSOR);
>> + if (!cursor) {
>> + DRM_ERROR("fail to init cursor plane\n");
>> + return -EINVAL;
>> + }
>> + } else {
>> + struct drm_plane *plane;
>> +
>> + plane = rockchip_plane_init(ctx, possible_crtcs,
>> + DRM_PLANE_TYPE_OVERLAY);
>> + if (!plane) {
>> + DRM_ERROR("fail to init overlay plane\n");
>> + return -EINVAL;
>> + }
>> + }
>> + }
>> +
>> + drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
>> + &rockchip_crtc_funcs);
>> + drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_vop_initial(struct vop_context *ctx)
>> +{
>> + struct vop_driver_data *vop_data = ctx->data;
>> + const struct vop_reg_data *init_table = vop_data->init_table;
>> + struct reset_control *rst;
>> + int i, ret;
>> +
>> + ctx->hclk = devm_clk_get(ctx->dev, "hclk_vop");
>> + if (IS_ERR(ctx->hclk)) {
>> + dev_err(ctx->dev, "failed to get hclk source\n");
>> + return PTR_ERR(ctx->hclk);
>> + }
>> + ctx->aclk = devm_clk_get(ctx->dev, "aclk_vop");
>> + if (IS_ERR(ctx->aclk)) {
>> + dev_err(ctx->dev, "failed to get aclk source\n");
>> + return PTR_ERR(ctx->aclk);
>> + }
>> + ctx->dclk = devm_clk_get(ctx->dev, "dclk_vop");
>> + if (IS_ERR(ctx->dclk)) {
>> + dev_err(ctx->dev, "failed to get dclk source\n");
>> + return PTR_ERR(ctx->dclk);
>> + }
>> +
>> + ret = vop_clk_enable(ctx);
>> + if (ret < 0)
>> + return ret;
> if anything fails after this, remember to disable the clk...
oh, yes, I will do disable clk if fail.
> Ok, that is enough for now :-)
>
> Thanks,
> -djk
Thanks very much for your review, nice suggestions.
-Mark
>
>> +
>> + /*
>> + * do hclk_reset, reset all vop registers.
>> + */
>> + rst = devm_reset_control_get(ctx->dev, "ahb");
>> + if (IS_ERR(rst)) {
>> + dev_err(ctx->dev, "failed to get ahb reset\n");
>> + return PTR_ERR(rst);
>> + }
>> + reset_control_assert(rst);
>> + usleep_range(10, 20);
>> + reset_control_deassert(rst);
>> +
>> + memcpy(ctx->regsbak, ctx->regs, ctx->len);
>> +
>> + for (i = 0; i < vop_data->table_size; i++)
>> + vop_writel(ctx, init_table[i].offset, init_table[i].value);
>> +
>> + for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++)
>> + VOP_WIN_SET(ctx, vop_data->win[i], enable, 0);
>> +
>> + vop_cfg_done(ctx);
>> +
>> + /*
>> + * do dclk_reset, let all win config take affect, and then we can enable
>> + * iommu safe.
>> + */
>> + rst = devm_reset_control_get(ctx->dev, "dclk");
>> + if (IS_ERR(rst)) {
>> + dev_err(ctx->dev, "failed to get dclk reset\n");
>> + return PTR_ERR(rst);
>> + }
>> + reset_control_assert(rst);
>> + usleep_range(10, 20);
>> + reset_control_deassert(rst);
>> +
>> + ctx->dpms = DRM_MODE_DPMS_ON;
>> +
>> + return 0;
>> +}
>> +
>> +static int vop_bind(struct device *dev, struct device *master, void *data)
>> +{
>> + struct platform_device *pdev = to_platform_device(dev);
>> + struct vop_driver_data *vop_data = vop_get_driver_data(dev);
>> + struct drm_device *drm_dev = data;
>> + struct vop_context *ctx;
>> + struct resource *res;
>> + int ret;
>> +
>> + if (!vop_data)
>> + return -ENODEV;
>> +
>> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
>> + if (!ctx)
>> + return -ENOMEM;
>> +
>> + ctx->dev = dev;
>> + ctx->data = vop_data;
>> + ctx->drm_dev = drm_dev;
>> + dev_set_drvdata(dev, ctx);
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + ctx->len = resource_size(res);
>> + ctx->regs = devm_ioremap_resource(dev, res);
>> + if (IS_ERR(ctx->regs))
>> + return PTR_ERR(ctx->regs);
>> +
>> + ctx->regsbak = devm_kzalloc(dev, ctx->len, GFP_KERNEL);
>> + if (!ctx->regsbak)
>> + return -ENOMEM;
>> +
>> + ret = rockchip_vop_initial(ctx);
>> + if (ret < 0) {
>> + dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ctx->irq = platform_get_irq(pdev, 0);
>> + if (ctx->irq < 0) {
>> + dev_err(dev, "cannot find irq for vop\n");
>> + return ctx->irq;
>> + }
>> +
>> + spin_lock_init(&ctx->reg_lock);
>> + spin_lock_init(&ctx->irq_lock);
>> +
>> + init_waitqueue_head(&ctx->wait_vsync_queue);
>> + atomic_set(&ctx->wait_vsync_event, 0);
>> +
>> + ret = vop_iommu_init(ctx);
>> + if (ret) {
>> + DRM_ERROR("Failed to setup iommu, %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ctx->vsync_wq = create_singlethread_workqueue("vsync");
>> + if (!ctx->vsync_wq) {
>> + dev_err(dev, "failed to create workqueue\n");
>> + return -EINVAL;
>> + }
>> + INIT_WORK(&ctx->vsync_work, rockchip_vsync_worker);
>> +
>> + mutex_init(&ctx->vsync_mutex);
>> + pm_runtime_enable(&pdev->dev);
>> +
>> + ret = devm_request_irq(dev, ctx->irq, rockchip_vop_isr,
>> + IRQF_SHARED, dev_name(dev), ctx);
>> + if (ret) {
>> + dev_err(dev, "cannot requeset irq%d - err %d\n", ctx->irq, ret);
>> + return ret;
>> + }
>> +
>> + return vop_create_crtc(ctx);
>> +}
>> +
>> +static void vop_unbind(struct device *dev, struct device *master,
>> + void *data)
>> +{
>> + struct drm_device *drm_dev = data;
>> + struct vop_context *ctx = dev_get_drvdata(dev);
>> + struct drm_crtc *crtc = &ctx->crtc;
>> +
>> + drm_crtc_cleanup(crtc);
>> + pm_runtime_disable(dev);
>> + rockchip_drm_remove_crtc(drm_dev, ctx->pipe);
>> +
>> + vop_iommu_fini(ctx);
>> +}
>> +
>> +static const struct component_ops vop_component_ops = {
>> + .bind = vop_bind,
>> + .unbind = vop_unbind,
>> +};
>> +
>> +static int vop_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct vop_context *ctx;
>> +
>> + if (!dev->of_node) {
>> + dev_err(dev, "can't find vop devices\n");
>> + return -ENODEV;
>> + }
>> +
>> + platform_set_drvdata(pdev, ctx);
>> +
>> + return component_add(dev, &vop_component_ops);
>> +}
>> +
>> +static int vop_remove(struct platform_device *pdev)
>> +{
>> + component_del(&pdev->dev, &vop_component_ops);
>> +
>> + return 0;
>> +}
>> +
>> +struct platform_driver rockchip_vop_platform_driver = {
>> + .probe = vop_probe,
>> + .remove = vop_remove,
>> + .driver = {
>> + .name = "rockchip-vop",
>> + .owner = THIS_MODULE,
>> + .of_match_table = of_match_ptr(vop_driver_dt_match),
>> + },
>> +};
>> +
>> +module_platform_driver(rockchip_vop_platform_driver);
>> +
>> +MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
>> +MODULE_DESCRIPTION("ROCKCHIP VOP Driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> new file mode 100644
>> index 0000000..2343760
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> @@ -0,0 +1,187 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef _ROCKCHIP_DRM_VOP_H
>> +#define _ROCKCHIP_DRM_VOP_H
>> +
>> +/* register definition */
>> +#define REG_CFG_DONE 0x0000
>> +#define VERSION_INFO 0x0004
>> +#define SYS_CTRL 0x0008
>> +#define SYS_CTRL1 0x000c
>> +#define DSP_CTRL0 0x0010
>> +#define DSP_CTRL1 0x0014
>> +#define DSP_BG 0x0018
>> +#define MCU_CTRL 0x001c
>> +#define INTR_CTRL0 0x0020
>> +#define INTR_CTRL1 0x0024
>> +#define WIN0_CTRL0 0x0030
>> +#define WIN0_CTRL1 0x0034
>> +#define WIN0_COLOR_KEY 0x0038
>> +#define WIN0_VIR 0x003c
>> +#define WIN0_YRGB_MST 0x0040
>> +#define WIN0_CBR_MST 0x0044
>> +#define WIN0_ACT_INFO 0x0048
>> +#define WIN0_DSP_INFO 0x004c
>> +#define WIN0_DSP_ST 0x0050
>> +#define WIN0_SCL_FACTOR_YRGB 0x0054
>> +#define WIN0_SCL_FACTOR_CBR 0x0058
>> +#define WIN0_SCL_OFFSET 0x005c
>> +#define WIN0_SRC_ALPHA_CTRL 0x0060
>> +#define WIN0_DST_ALPHA_CTRL 0x0064
>> +#define WIN0_FADING_CTRL 0x0068
>> +/* win1 register */
>> +#define WIN1_CTRL0 0x0070
>> +#define WIN1_CTRL1 0x0074
>> +#define WIN1_COLOR_KEY 0x0078
>> +#define WIN1_VIR 0x007c
>> +#define WIN1_YRGB_MST 0x0080
>> +#define WIN1_CBR_MST 0x0084
>> +#define WIN1_ACT_INFO 0x0088
>> +#define WIN1_DSP_INFO 0x008c
>> +#define WIN1_DSP_ST 0x0090
>> +#define WIN1_SCL_FACTOR_YRGB 0x0094
>> +#define WIN1_SCL_FACTOR_CBR 0x0098
>> +#define WIN1_SCL_OFFSET 0x009c
>> +#define WIN1_SRC_ALPHA_CTRL 0x00a0
>> +#define WIN1_DST_ALPHA_CTRL 0x00a4
>> +#define WIN1_FADING_CTRL 0x00a8
>> +/* win2 register */
>> +#define WIN2_CTRL0 0x00b0
>> +#define WIN2_CTRL1 0x00b4
>> +#define WIN2_VIR0_1 0x00b8
>> +#define WIN2_VIR2_3 0x00bc
>> +#define WIN2_MST0 0x00c0
>> +#define WIN2_DSP_INFO0 0x00c4
>> +#define WIN2_DSP_ST0 0x00c8
>> +#define WIN2_COLOR_KEY 0x00cc
>> +#define WIN2_MST1 0x00d0
>> +#define WIN2_DSP_INFO1 0x00d4
>> +#define WIN2_DSP_ST1 0x00d8
>> +#define WIN2_SRC_ALPHA_CTRL 0x00dc
>> +#define WIN2_MST2 0x00e0
>> +#define WIN2_DSP_INFO2 0x00e4
>> +#define WIN2_DSP_ST2 0x00e8
>> +#define WIN2_DST_ALPHA_CTRL 0x00ec
>> +#define WIN2_MST3 0x00f0
>> +#define WIN2_DSP_INFO3 0x00f4
>> +#define WIN2_DSP_ST3 0x00f8
>> +#define WIN2_FADING_CTRL 0x00fc
>> +/* win3 register */
>> +#define WIN3_CTRL0 0x0100
>> +#define WIN3_CTRL1 0x0104
>> +#define WIN3_VIR0_1 0x0108
>> +#define WIN3_VIR2_3 0x010c
>> +#define WIN3_MST0 0x0110
>> +#define WIN3_DSP_INFO0 0x0114
>> +#define WIN3_DSP_ST0 0x0118
>> +#define WIN3_COLOR_KEY 0x011c
>> +#define WIN3_MST1 0x0120
>> +#define WIN3_DSP_INFO1 0x0124
>> +#define WIN3_DSP_ST1 0x0128
>> +#define WIN3_SRC_ALPHA_CTRL 0x012c
>> +#define WIN3_MST2 0x0130
>> +#define WIN3_DSP_INFO2 0x0134
>> +#define WIN3_DSP_ST2 0x0138
>> +#define WIN3_DST_ALPHA_CTRL 0x013c
>> +#define WIN3_MST3 0x0140
>> +#define WIN3_DSP_INFO3 0x0144
>> +#define WIN3_DSP_ST3 0x0148
>> +#define WIN3_FADING_CTRL 0x014c
>> +/* hwc register */
>> +#define HWC_CTRL0 0x0150
>> +#define HWC_CTRL1 0x0154
>> +#define HWC_MST 0x0158
>> +#define HWC_DSP_ST 0x015c
>> +#define HWC_SRC_ALPHA_CTRL 0x0160
>> +#define HWC_DST_ALPHA_CTRL 0x0164
>> +#define HWC_FADING_CTRL 0x0168
>> +/* post process register */
>> +#define POST_DSP_HACT_INFO 0x0170
>> +#define POST_DSP_VACT_INFO 0x0174
>> +#define POST_SCL_FACTOR_YRGB 0x0178
>> +#define POST_SCL_CTRL 0x0180
>> +#define POST_DSP_VACT_INFO_F1 0x0184
>> +#define DSP_HTOTAL_HS_END 0x0188
>> +#define DSP_HACT_ST_END 0x018c
>> +#define DSP_VTOTAL_VS_END 0x0190
>> +#define DSP_VACT_ST_END 0x0194
>> +#define DSP_VS_ST_END_F1 0x0198
>> +#define DSP_VACT_ST_END_F1 0x019c
>> +/* register definition end */
>> +
>> +/* interrupt define */
>> +#define DSP_HOLD_VALID_INTR (1 << 0)
>> +#define FS_INTR (1 << 1)
>> +#define LINE_FLAG_INTR (1 << 2)
>> +#define BUS_ERROR_INTR (1 << 3)
>> +
>> +#define DSP_HOLD_VALID_INTR_EN(x) ((x) << 4)
>> +#define FS_INTR_EN(x) ((x) << 5)
>> +#define LINE_FLAG_INTR_EN(x) ((x) << 6)
>> +#define BUS_ERROR_INTR_EN(x) ((x) << 7)
>> +#define DSP_HOLD_VALID_INTR_MASK (1 << 4)
>> +#define FS_INTR_EN_MASK (1 << 5)
>> +#define LINE_FLAG_INTR_MASK (1 << 6)
>> +#define BUS_ERROR_INTR_MASK (1 << 7)
>> +
>> +#define DSP_HOLD_VALID_INTR_CLR (1 << 8)
>> +#define FS_INTR_EN_CLR (1 << 9)
>> +#define LINE_FLAG_INTR_CLR (1 << 10)
>> +#define BUS_ERROR_INTR_CLR (1 << 11)
>> +#define DSP_LINE_NUM(x) (((x) & 0x1fff) << 12)
>> +#define DSP_LINE_NUM_MASK (0x1fff << 12)
>> +
>> +/* src alpha ctrl define */
>> +#define SRC_FADING_VALUE(x) (((x) & 0xff) << 24)
>> +#define SRC_GLOBAL_ALPHA(x) (((x) & 0xff) << 16)
>> +#define SRC_FACTOR_M0(x) (((x) & 0x7) << 6)
>> +#define SRC_ALPHA_CAL_M0(x) (((x) & 0x1) << 5)
>> +#define SRC_BLEND_M0(x) (((x) & 0x3) << 3)
>> +#define SRC_ALPHA_M0(x) (((x) & 0x1) << 2)
>> +#define SRC_COLOR_M0(x) (((x) & 0x1) << 1)
>> +#define SRC_ALPHA_EN(x) (((x) & 0x1) << 0)
>> +/* dst alpha ctrl define */
>> +#define DST_FACTOR_M0(x) (((x) & 0x7) << 6)
>> +
>> +enum alpha_mode {
>> + ALPHA_STRAIGHT,
>> + ALPHA_INVERSE,
>> +};
>> +
>> +enum global_blend_mode {
>> + ALPHA_GLOBAL,
>> + ALPHA_PER_PIX,
>> + ALPHA_PER_PIX_GLOBAL,
>> +};
>> +
>> +enum alpha_cal_mode {
>> + ALPHA_SATURATION,
>> + ALPHA_NO_SATURATION,
>> +};
>> +
>> +enum color_mode {
>> + ALPHA_SRC_PRE_MUL,
>> + ALPHA_SRC_NO_PRE_MUL,
>> +};
>> +
>> +enum factor_mode {
>> + ALPHA_ZERO,
>> + ALPHA_ONE,
>> + ALPHA_SRC,
>> + ALPHA_SRC_INVERSE,
>> + ALPHA_SRC_GLOBAL,
>> +};
>> +
>> +#endif /* _ROCKCHIP_DRM_VOP_H */
>> --
>> 1.7.9.5
>>
>
>
[-- Attachment #1.2: Type: text/html, Size: 140434 bytes --]
[-- Attachment #2: Type: text/plain, Size: 159 bytes --]
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Nicolas Dichtel @ 2014-09-25 8:53 UTC (permalink / raw)
To: Cong Wang
Cc: netdev, containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Andy Lutomirski, Stephen Hemminger, Eric W. Biederman,
linux-api-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, David Miller
In-Reply-To: <CAHA+R7Ot2kLHb+ne5AW3bFNc87qkFOiEHEyDh9sFrsHqXgrY3g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Le 24/09/2014 18:45, Cong Wang a écrit :
> On Wed, Sep 24, 2014 at 9:27 AM, Nicolas Dichtel
> <nicolas.dichtel@6wind.com> wrote:
>> Now informations got with 'ip link' are wrong and incomplete:
>> - the link dev is now tunl0 instead of eth0, because we only got an
>> ifindex
>> from the kernel without any netns informations.
>
> This is not new, macvlan has the same problem. This is why I said
> it is mostly a display problem, maybe just mark the ifindex as -1 or
> something when it is not in this netns. At least I don't expect the inner
> netns know anything outside, and I don't think I am the only one using
> netns in this way.
I understand your point but there is several use of netns. Netns can be used
also to instantiate virtual routers. In this case, administrators or daemons
need to be able to monitor and dump the configuration on all netns
(particularly beeing able to identify fully x-netns interfaces). We start to
discuss this in one of the two thread pointed in my cover letter and get the
conclusion that checking user ns is a good way to know if an id should be
disclosed or not for a peer netns.
Can you describe your use case?
>
>> - the encapsulation addresses are not part of this netns but the user
>> doesn't
>> known that (still because netns info is missing). These IPv4 addresses
>> may
>> exist into this netns.
>
> I don't remember your x-netns code, but we have two choices:
>
> 1) Lookup the route of the netns which it is in
>
> If the address is not available in this netns, it will fail, this is expected
> since tunnel device is not a pure L2 device. Or maybe just fail
> early when we move it.
>
> 2) Lookup the route of the netns where it was created
>
> Transparent for upper layer, but as you said, the outer address is not
> available in this netns therefore hard to display. Just hiding this information
> doesn't seem wrong to me.
Your assumption here is that all dameons were started before the tunnel was
created. But this is not true, a daemon may be started later. Another case is
when a daemon crash: we need to be able to restart it and it should be able to
recover all needed information.
>
>
>> - it's not possible to create the same netdevice with these infos.
>>
>
> This is expected, because after all you are already in a different netns.
>
A different netns only means a different network stack, not a different user ns
or mount ns or PID ns, ...
If you only play with netns, you may want to monitor all activies in all netns
(this is already possible) and beeing able to link information between netns
(this is what I'm trying to solve).
_______________________________________________
Containers mailing list
Containers@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/containers
^ permalink raw reply
* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Nicolas Dichtel @ 2014-09-25 8:53 UTC (permalink / raw)
To: Cong Wang
Cc: netdev, containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Andy Lutomirski, Stephen Hemminger, Eric W. Biederman,
linux-api-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, David Miller
In-Reply-To: <CAHA+R7MM04ew=J8sHfSQERwoCAgEDfrGgtgRORqs3ePgtjVYWg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Le 24/09/2014 18:48, Cong Wang a écrit :
> On Wed, Sep 24, 2014 at 9:31 AM, Nicolas Dichtel
> <nicolas.dichtel@6wind.com> wrote:
>>> I think in this case your ID's are still available, but aren't you
>>> providing a new way
>>> for the inner netns device to escape which we are trying to avoid?
>>
>> It's why the ids depend on user ns. Only if user ns are the same we allow to
>> get an id for a peer netns.
>
> Too late, userns is relatively new, relying on it breaks our existing
> assumption.
>
I don't get your point. netns has been added in kernel after user ns:
acce292c82d4 user namespace: add the framework => 2.6.23
5f256becd868 [NET]: Basic network namespace infrastructure. => 2.6.24
In the kernel, each netns is linked with a user ns.
_______________________________________________
Containers mailing list
Containers@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/containers
^ permalink raw reply
* Re: [PATCH 4/4] Adds ioctl interface support for ext4 project
From: Dave Chinner @ 2014-09-25 7:59 UTC (permalink / raw)
To: Jan Kara
Cc: adilger, tytso, linux-api, xfs, Christoph Hellwig, dmonakhov,
viro, Li Xi, linux-fsdevel, linux-ext4
In-Reply-To: <20140924170105.GE27000@quack.suse.cz>
On Wed, Sep 24, 2014 at 07:01:05PM +0200, Jan Kara wrote:
> On Wed 24-09-14 09:26:34, Christoph Hellwig wrote:
> > On Wed, Sep 24, 2014 at 06:25:07PM +0200, Jan Kara wrote:
> > > On Wed 24-09-14 22:04:30, Li Xi wrote:
> > > > This patch adds ioctl interface for setting/getting project of ext4.
> > > The patch looks good to me. I was just wondering whether it won't be
> > > useful to add an ioctl() which isn't ext4 specific. We could just extend
> > > ->setattr() to allow setting of project ID (most filesystems would just
> > > return -EOPNOTSUPP but ext4 and xfs could do the right thing) and then call
> > > ->setattr from the generic ioctl. That way userspace won't have to care
> > > about filesystem type when setting project ID... What do others think?
> >
> > Absolutely. In general I also wonder why this patch doesn't implement
> > the full XFS API. Maybe there is a reason it was considered and
> > rejected, but it would be helpful to document why.
> Do you mean full get/setfsxattr API?
That's a good start.
The bigger issue in my mind is that we already have a fully featured
quota API that supports project quotas and userspace tools available
that manipulate it. xfstests already uses those tools and API
for testing project quotas.
This whole patchset reinvents all the quota APIs, and will require
adding support in userspace, and hence require re-inventing all the
test infrastructure we already have because it won't be compatible
with the existing project quota test code.
> That basically contains project ID,
> flags (those that are currently get/set with FS_IOC_GETFLAGS/SETFLAGS), and
> extent size hint right?
It's a different set of flag definitions. We translate the interface
XFS_XFLAG_* values to/from the inode on-disk XFS_DIFLAG_* inode values, just
like we translate the VFS FS_*_FL flags that get passed through the
FS_IOC_GETFLAGS/SETFLAGS ioctl.
> That seems workable and it would also make setting
> of PROJINHERIT flag fs agnostic. Only we would have to create some generic
> flags namespace and merge into that ext4 flags and have a translation
> function for the old ext4 flags.
The XFS_XFLAGS_* are already filesystem agnostic - they are flags
that are only used for interfacing with userspace and hence only
exist at the ioctl copy in/out layer.....
> Also I'm afraid we may quickly run out of
> 32 available flags in xflags so we'd need to extend that. But all this
> seems to be doable.
The struct fsxattr was designed to be extensible - it has unused
padding and enough space in the flags field to allow us to
conditionally use that padding....
Cheers,
Dave.
--
Dave Chinner
david@fromorbit.com
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply
* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Nicolas Dichtel @ 2014-09-25 7:54 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Network Development, Linux Containers,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Linux API,
David S. Miller, Eric W. Biederman, Stephen Hemminger,
Andrew Morton
In-Reply-To: <CALCETrXMkV_1XR2mXOoO_2_vpB=6cPbWAAOhPFhPbDa_1P+zxw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Le 24/09/2014 19:05, Andy Lutomirski a écrit :
> On Wed, Sep 24, 2014 at 2:31 AM, Nicolas Dichtel
> <nicolas.dichtel-pdR9zngts4EAvxtiuMwx3w@public.gmane.org> wrote:
>> Le 23/09/2014 21:26, Andy Lutomirski a écrit :
>>
>>> On Tue, Sep 23, 2014 at 6:20 AM, Nicolas Dichtel
>>> <nicolas.dichtel-pdR9zngts4EAvxtiuMwx3w@public.gmane.org> wrote:
>>>>
>>>> The goal of this serie is to be able to multicast netlink messages with
>>>> an
>>>> attribute that identify a peer netns.
>>>> This is needed by the userland to interpret some informations contained
>>>> in
>>>> netlink messages (like IFLA_LINK value, but also some other attributes in
>>>> case
>>>> of x-netns netdevice (see also
>>>> http://thread.gmane.org/gmane.linux.network/315933/focus=316064 and
>>>> http://thread.gmane.org/gmane.linux.kernel.containers/28301/focus=4239)).
>>>>
>>>> Ids are stored in the parent user namespace. These ids are valid only
>>>> inside
>>>> this user namespace. The user can retrieve these ids via a new netlink
>>>> messages,
>>>> but only if peer netns are in the same user namespace.
>>>
>>>
>>> What about the parent / ancestors of the owning userns? Can processes
>>> in those usernses see any form of netns id?
>>
>> With this serie no. I'm not sure if ancestors really needs to be able to
>> get these ids. What is your opinion?
>
> I might be missing some consideration here, but I would hope that ip
> link would work correctly if I have a veth interface shared with a
> netns that's in a child userns.
No, you're right. Will send a v3.
^ permalink raw reply
* Re: [PATCH 4/4] Adds ioctl interface support for ext4 project
From: Dave Chinner @ 2014-09-25 7:26 UTC (permalink / raw)
To: Jan Kara
Cc: adilger, tytso, linux-api, xfs, hch, dmonakhov, viro, Li Xi,
linux-fsdevel, linux-ext4
In-Reply-To: <20140924162507.GC27000@quack.suse.cz>
On Wed, Sep 24, 2014 at 06:25:07PM +0200, Jan Kara wrote:
> On Wed 24-09-14 22:04:30, Li Xi wrote:
> > This patch adds ioctl interface for setting/getting project of ext4.
> The patch looks good to me. I was just wondering whether it won't be
> useful to add an ioctl() which isn't ext4 specific. We could just extend
> ->setattr() to allow setting of project ID (most filesystems would just
> return -EOPNOTSUPP but ext4 and xfs could do the right thing) and then call
> ->setattr from the generic ioctl. That way userspace won't have to care
> about filesystem type when setting project ID... What do others think?
I've repeatedly said that these ext4 project ID patches should
implement the same interfaces as XFS rather than creating a new set
of incompatible, ext4 specific interfaces to do implement the same
functionality.
There is no good reason for forcing userspace to re-invent tools
that already exist just to manage identical functionality in
different filesystems.
Cheers,
Dave.
--
Dave Chinner
david@fromorbit.com
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply
* Re: [RFC v3 0/4] vfs: Non-blockling buffered fs read (page cache only)
From: Michael Kerrisk @ 2014-09-25 4:06 UTC (permalink / raw)
To: Milosz Tanski
Cc: Linux Kernel, Christoph Hellwig, Linux-Fsdevel, linux-aio,
Mel Gorman, Volker Lendecke, Tejun Heo, Jeff Moyer,
Theodore Ts'o, Al Viro, Linux API
In-Reply-To: <cover.1411594644.git.milosz-B5zB6C1i6pkAvxtiuMwx3w@public.gmane.org>
Hello Milosz,
On Wed, Sep 24, 2014 at 11:46 PM, Milosz Tanski <milosz-B5zB6C1i6pkAvxtiuMwx3w@public.gmane.org> wrote:
> This patcheset introduces an ability to perform a non-blocking read from
> regular files in buffered IO mode. This works by only for those filesystems
> that have data in the page cache.
>
> It does this by introducing new syscalls new syscalls preadv2/pwritev2. These
> new syscalls behave like the network sendmsg, recvmsg syscalls that accept an
> extra flag argument (RWF_NONBLOCK).
>
> It's a very common patern today (samba, libuv, etc..) use a large threadpool to
> perform buffered IO operations. They submit the work form another thread
> that performs network IO and epoll or other threads that perform CPU work. This
> leads to increased latency for processing, esp. in the case of data that's
> already cached in the page cache.
>
> With the new interface the applications will now be able to fetch the data in
> their network / cpu bound thread(s) and only defer to a threadpool if it's not
> there. In our own application (VLDB) we've observed a decrease in latency for
> "fast" request by avoiding unnecessary queuing and having to swap out current
> tasks in IO bound work threads.
Since this is a change to the user-space API, could you CC future
versions of this patch set to linux-api-U6gemL7RCxhzeIdxy0IIJw@public.gmane.org please, as
per Documentation/SubmitChecklist. See also
https://www.kernel.org/doc/man-pages/linux-api-ml.html.
Thanks,
Michael
> Version 3 highlights:
> - Down to 2 syscalls from 4; can user fp or argument position.
> - RWF_NONBLOCK value flag is not the same O_NONBLOCK, per Jeff.
>
> Version 2 highlights:
> - Put the flags argument into kiocb (less noise), per. Al Viro
> - O_DIRECT checking early in the process, per. Jeff Moyer
> - Resolved duplicate (c&p) code in syscall code, per. Jeff
> - Included perf data in thread cover letter, per. Jeff
> - Created a new flag (not O_NONBLOCK) for readv2, perf Jeff
>
>
> Some perf data generated using fio comparing the posix aio engine to a version
> of the posix AIO engine that attempts to performs "fast" reads before
> submitting the operations to the queue. This workflow is on ext4 partition on
> raid0 (test / build-rig.) Simulating our database access patern workload using
> 16kb read accesses. Our database uses a home-spun posix aio like queue (samba
> does the same thing.)
>
> f1: ~73% rand read over mostly cached data (zipf med-size dataset)
> f2: ~18% rand read over mostly un-cached data (uniform large-dataset)
> f3: ~9% seq-read over large dataset
>
> before:
>
> f1:
> bw (KB /s): min= 11, max= 9088, per=0.56%, avg=969.54, stdev=827.99
> lat (msec) : 50=0.01%, 100=1.06%, 250=5.88%, 500=4.08%, 750=12.48%
> lat (msec) : 1000=17.27%, 2000=49.86%, >=2000=9.42%
> f2:
> bw (KB /s): min= 2, max= 1882, per=0.16%, avg=273.28, stdev=220.26
> lat (msec) : 250=5.65%, 500=3.31%, 750=15.64%, 1000=24.59%, 2000=46.56%
> lat (msec) : >=2000=4.33%
> f3:
> bw (KB /s): min= 0, max=265568, per=99.95%, avg=174575.10,
> stdev=34526.89
> lat (usec) : 2=0.01%, 4=0.01%, 10=0.02%, 20=0.27%, 50=10.82%
> lat (usec) : 100=50.34%, 250=5.05%, 500=7.12%, 750=6.60%, 1000=4.55%
> lat (msec) : 2=8.73%, 4=3.49%, 10=1.83%, 20=0.89%, 50=0.22%
> lat (msec) : 100=0.05%, 250=0.02%, 500=0.01%
> total:
> READ: io=102365MB, aggrb=174669KB/s, minb=240KB/s, maxb=173599KB/s,
> mint=600001msec, maxt=600113msec
>
> after (with fast read using preadv2 before submit):
>
> f1:
> bw (KB /s): min= 3, max=14897, per=1.28%, avg=2276.69, stdev=2930.39
> lat (usec) : 2=70.63%, 4=0.01%
> lat (msec) : 250=0.20%, 500=2.26%, 750=1.18%, 2000=0.22%, >=2000=25.53%
> f2:
> bw (KB /s): min= 2, max= 2362, per=0.14%, avg=249.83, stdev=222.00
> lat (msec) : 250=6.35%, 500=1.78%, 750=9.29%, 1000=20.49%, 2000=52.18%
> lat (msec) : >=2000=9.99%
> f3:
> bw (KB /s): min= 1, max=245448, per=100.00%, avg=177366.50,
> stdev=35995.60
> lat (usec) : 2=64.04%, 4=0.01%, 10=0.01%, 20=0.06%, 50=0.43%
> lat (usec) : 100=0.20%, 250=1.27%, 500=2.93%, 750=3.93%, 1000=7.35%
> lat (msec) : 2=14.27%, 4=2.88%, 10=1.54%, 20=0.81%, 50=0.22%
> lat (msec) : 100=0.05%, 250=0.02%
> total:
> READ: io=103941MB, aggrb=177339KB/s, minb=213KB/s, maxb=176375KB/s,
> mint=600020msec, maxt=600178msec
>
> Interpreting the results you can see total bandwidth stays the same but overall
> request latency is decreased in f1 (random, mostly cached) and f3 (sequential)
> workloads. There is a slight bump in latency for since it's random data that's
> unlikely to be cached but we're always trying "fast read".
>
> In our application we have starting keeping track of "fast read" hits/misses
> and for files / requests that have a lot hit ratio we don't do "fast reads"
> mostly getting rid of extra latency in the uncached cases.
>
> I've performed other benchmarks and I have no observed any perf regressions in
> any of the normal (old) code paths.
>
>
> I have co-developed these changes with Christoph Hellwig.
>
> Milosz Tanski (4):
> vfs: Prepare for adding a new preadv/pwritev with user flags.
> vfs: Define new syscalls preadv2,pwritev2
> vfs: Export new vector IO syscalls (with flags) to userland
> vfs: RWF_NONBLOCK flag for preadv2
>
> arch/x86/syscalls/syscall_32.tbl | 2 +
> arch/x86/syscalls/syscall_64.tbl | 2 +
> drivers/target/target_core_file.c | 6 +-
> fs/cifs/file.c | 6 ++
> fs/nfsd/vfs.c | 4 +-
> fs/ocfs2/file.c | 6 ++
> fs/pipe.c | 3 +-
> fs/read_write.c | 121 +++++++++++++++++++++++++++++---------
> fs/splice.c | 2 +-
> fs/xfs/xfs_file.c | 4 ++
> include/linux/aio.h | 2 +
> include/linux/fs.h | 7 ++-
> include/linux/syscalls.h | 6 ++
> include/uapi/asm-generic/unistd.h | 6 +-
> mm/filemap.c | 22 ++++++-
> mm/shmem.c | 4 ++
> 16 files changed, 163 insertions(+), 40 deletions(-)
>
> --
> 2.1.0
>
> --
> To unsubscribe, send a message with 'unsubscribe linux-aio' in
> the body to majordomo-Bw31MaZKKs0EbZ0PF+XxCw@public.gmane.org For more info on Linux AIO,
> see: http://www.kvack.org/aio/
> Don't email: <a href=mailto:"aart-Bw31MaZKKs3YtjvyW6yDsg@public.gmane.org">aart-Bw31MaZKKs3YtjvyW6yDsg@public.gmane.org</a>
--
Michael Kerrisk Linux man-pages maintainer;
http://www.kernel.org/doc/man-pages/
Author of "The Linux Programming Interface", http://blog.man7.org/
^ permalink raw reply
* Re: [PATCH 3/4] Adds project quota support for ext4
From: Li Xi @ 2014-09-25 1:28 UTC (permalink / raw)
To: Jan Kara
Cc: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Ext4 Developers List,
linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Theodore Ts'o, Andreas Dilger,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org,
hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org, Dmitry Monakhov
In-Reply-To: <20140924173123.GH27000-+0h/O2h83AeN3ZZ/Hiejyg@public.gmane.org>
On Thu, Sep 25, 2014 at 1:31 AM, Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org> wrote:
> On Wed 24-09-14 22:04:29, Li Xi wrote:
>> This patch adds mount options for enabling/disabling project quota
>> accounting and enforcement. A new specific inode is also used for
>> project quota accounting.
> The patch looks mostly fine. A few smaller things below.
>
> ...
>> @@ -1433,6 +1437,8 @@ static const struct mount_opts {
>> MOPT_SET | MOPT_Q},
>> {Opt_grpquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_GRPQUOTA,
>> MOPT_SET | MOPT_Q},
>> + {Opt_prjquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_PRJQUOTA,
>> + MOPT_SET | MOPT_Q},
>> {Opt_noquota, (EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
>> EXT4_MOUNT_GRPQUOTA), MOPT_CLEAR | MOPT_Q},
> I think you missed to add EXT4_MOUNT_PRJQUOTA to Opt_noquota...
>
> ...
>> @@ -2833,6 +2855,13 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
>> "without CONFIG_QUOTA");
>> return 0;
>> }
>> + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT) &&
>> + !readonly) {
>> + ext4_msg(sb, KERN_ERR,
>> + "Filesystem with project quota feature cannot be"
>> + "mounted RDWR without CONFIG_QUOTA");
>> + return 0;
>> + }
> Hum, I don't think this is right. EXT4_FEATURE_RO_COMPAT_PROJECT is about
> maintaining project IDs not about quota. So it seems perfectly OK to have
> EXT4_FEATURE_RO_COMPAT_PROJECT without CONFIG_QUOTA.
Ah, I see. This might be my main misunderstanding. I thought it is
about maintaining
both project IDs and quota. And I misunderstood so that I removed all
EXT4_FEATURE_RO_COMPAT_PROJECT checking when set/get project ID.
If we only use EXT4_FEATURE_RO_COMPAT_PROJECT to protect imcompatibility
of project ID, what about the change of struct ext4_super_block? I am
still confused.
Please advise.
Regards,
Li Xi
^ permalink raw reply
* Re: [PATCH 2/4] Adds project ID support for ext4
From: Li Xi @ 2014-09-25 1:09 UTC (permalink / raw)
To: Jan Kara
Cc: linux-fsdevel@vger.kernel.org, Ext4 Developers List,
linux-api@vger.kernel.org, Theodore Ts'o, Andreas Dilger,
viro@zeniv.linux.org.uk, hch@infradead.org, Dmitry Monakhov
In-Reply-To: <20140924171108.GG27000@quack.suse.cz>
On Thu, Sep 25, 2014 at 1:11 AM, Jan Kara <jack@suse.cz> wrote:
> On Wed 24-09-14 22:04:28, Li Xi wrote:
>> This patch adds a new internal field of ext4 inode to save project
>> identifier. Also a new flag EXT4_INODE_PROJINHERIT is added for
>> inheriting project ID from parent directory.
> Nice. Just a few smaller things below.
>
> Firstly, I think your patch forgets to add a check to EXT4_IOC_SETFLAGS
> ioctl so that EXT4_INODE_PROJINHERIT flag can be set only when
> EXT4_FEATURE_RO_COMPAT_PROJECT is enabled.
PROJINHERIT is a flag which affects the project ID inheriting. Is there
any special reason that this flag can be set/cleared only if
EXT4_FEATURE_RO_COMPAT_PROJECT is enabled? We don't check
that feature even when set/get project ID now, so I am not so sure
that this is consistent.
>
>> Signed-off-by: Li Xi <lixi@ddn.com>
>> ---
>> fs/ext4/ext4.h | 21 +++++++++++++++++----
>> fs/ext4/ialloc.c | 6 ++++++
>> fs/ext4/inode.c | 29 ++++++++++++++++++++++++++++-
>> fs/ext4/namei.c | 10 ++++++++++
>> fs/ext4/super.c | 1 +
>> 5 files changed, 62 insertions(+), 5 deletions(-)
>>
> ...
>> diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
>> index 5b87fc3..b6b7284 100644
>> --- a/fs/ext4/ialloc.c
>> +++ b/fs/ext4/ialloc.c
>> @@ -756,6 +756,12 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
>> inode->i_gid = dir->i_gid;
>> } else
>> inode_init_owner(inode, dir, mode);
>> + if (ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT)) {
>> + ei->i_projid = EXT4_I(dir)->i_projid;
>> + } else {
>> + ei->i_projid =
>> + make_kprojid(&init_user_ns, EXT4_DEF_PROJID);
>> + }
>> dquot_initialize(inode);
>>
>> if (!goal)
>> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
>> index d5dd7d4..8c641eb 100644
>> --- a/fs/ext4/inode.c
>> +++ b/fs/ext4/inode.c
>> @@ -3874,6 +3874,24 @@ static inline void ext4_iget_extra_inode(struct inode *inode,
>> EXT4_I(inode)->i_inline_off = 0;
>> }
>>
>> +static projid_t ext4_inode_projid_get(struct inode *inode, struct ext4_inode *raw,
>> + struct ext4_inode_info *ei)
>> +{
>> + return (projid_t)le32_to_cpu(raw->i_projid);
>> +}
>> +
>> +static void ext4_inode_projid_set(struct inode *inode, struct ext4_inode *raw,
>> + struct ext4_inode_info *ei, projid_t projid)
>> +{
>> + raw->i_projid = cpu_to_le32(projid);
>> +}
> I don't see a need for these two wrappers. IMHO they just obfuscate the
> code. Please just write the code directly in appropriate place.
Sure.
>
>> @@ -4204,7 +4228,8 @@ static int ext4_do_update_inode(handle_t *handle,
>> EXT4_INODE_SET_XTIME(i_atime, inode, raw_inode);
>> EXT4_EINODE_SET_XTIME(i_crtime, ei, raw_inode);
>>
>> - if (ext4_inode_blocks_set(handle, raw_inode, ei)) {
>> + err = ext4_inode_blocks_set(handle, raw_inode, ei);
>> + if (err) {
>> spin_unlock(&ei->i_raw_lock);
>> goto out_brelse;
>> }
> This is an unrelated fix, right? Please send as a separate patch. Thanks!
No problem!
>
>> @@ -4255,6 +4280,8 @@ static int ext4_do_update_inode(handle_t *handle,
>>
>> ext4_inode_csum_set(inode, raw_inode, ei);
>>
>> + ext4_inode_projid_set(inode, raw_inode, ei, i_projid);
>> +
> This is wrong - you have to set project ID before a checksum is computed.
Yeah, sorry for this mistake.
Regards,
- Li Xi
^ permalink raw reply
* Re: [PATCH v4 1/5] drm/rockchip: Add basic drm driver
From: Mark yao @ 2014-09-25 0:54 UTC (permalink / raw)
To: Daniel Vetter
Cc: Mark Rutland, Heiko Stübner, linux-doc, Kever Yang,
dri-devel, Linux Kernel Mailing List, Jianqun Xu, Chris Zhong,
linux-api, jeff chen, linux-rockchip, kfx, Grant Likely,
王晓腾, Tao Huang, devicetree@vger.kernel.org,
Pawel Moll, Ian Campbell, yxj, Rob Herring,
Stéphane Marchesin, simon xue, xw
In-Reply-To: <CAKMK7uHiQFDH-qyPBsyVa953F5SY2UgSv7Nbsu73AKvjqUm1jw@mail.gmail.com>
[-- Attachment #1.1: Type: text/plain, Size: 2976 bytes --]
On 2014年09月24日 19:20, Daniel Vetter wrote:
> On Wed, Sep 24, 2014 at 11:31 AM, Mark yao <mark.yao@rock-chips.com> wrote:
>> On 2014年09月24日 16:20, Daniel Vetter wrote:
>>> On Mon, Sep 22, 2014 at 06:48:54PM +0800, Mark yao wrote:
>>>> This patch adds the basic structure of a DRM Driver for Rockchip Socs.
>>>>
>>>> Signed-off-by: Mark yao <mark.yao@rock-chips.com>
>>>> ---
>>>> Changes in v2:
>>>> - use the component framework to defer main drm driver probe
>>>> until all VOP devices have been probed.
>>>> - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
>>>> master device and each vop device can shared the drm dma mapping.
>>>> - use drm_crtc_init_with_planes and drm_universal_plane_init.
>>>> - remove unnecessary middle layers.
>>>> - add cursor set, move funcs to rockchip drm crtc.
>>>> - use vop reset at first init
>>>> - reference framebuffer when used and unreference when swap out vop
>>>>
>>>> Changes in v3:
>>>> - change "crtc->fb" to "crtc->primary-fb"
>>>> Adviced by Daniel Vetter
>>>> - init cursor plane with universal api, remove unnecessary cursor
>>>> set,move
>>>>
>>>> Changes in v4:
>>>> Adviced by David Herrmann
>>>> - remove drm_platform_*() usage, use register drm device directly.
>>>> Adviced by Rob Clark
>>>> - remove special mmap ioctl, do userspace mmap with normal mmap() or mmap
>>>> offset
>>>>
>>>> drivers/gpu/drm/Kconfig | 2 +
>>>> drivers/gpu/drm/Makefile | 1 +
>>>> drivers/gpu/drm/rockchip/Kconfig | 19 +
>>>> drivers/gpu/drm/rockchip/Makefile | 10 +
>>>> drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 524 ++++++++++
>>>> drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 120 +++
>>>> drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 201 ++++
>>>> drivers/gpu/drm/rockchip/rockchip_drm_fb.h | 28 +
>>>> drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 231 +++++
>>>> drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 20 +
>>>> drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 404 ++++++++
>>>> drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 72 ++
>>>> drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 1372
>>>> +++++++++++++++++++++++++
>>>> drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 187 ++++
>>>> include/uapi/drm/rockchip_drm.h | 75 ++
>>> uapi is still here ... Was this an oversight?
>>> -Daniel
>>>
>> Hi, Daniel
>> this version is old, newest is v5. and I remove uapi at v5.
>> you can see v5 patch at:
>> https://lkml.org/lkml/2014/9/23/1061
>> thanks
> This version doesn't seem to be cc'ed to dri-devel, at least it didn't
> yet show up. Can you please double-check?
actually I cc the v5 version to dri-devel@lists.freedesktop.org.
and we can found the patch at
https://patchwork.kernel.org/patch/4967501/( *Project*: dri-devel)
>
> Thanks, Daniel
>
>
>
[-- Attachment #1.2: Type: text/html, Size: 5169 bytes --]
[-- Attachment #2: Type: text/plain, Size: 159 bytes --]
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [PATCH v4 3/4] quota: add project quota support
From: Li Xi @ 2014-09-24 23:56 UTC (permalink / raw)
To: Andreas Dilger
Cc: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Ext4 Developers List,
linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Theodore Ts'o, Jan Kara,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org,
hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org, Dmitry Monakhov
In-Reply-To: <8EDAF5F3-347B-4E6C-AEFF-9643E957B830-m1MBpc4rdrD3fQ9qLvQP4Q@public.gmane.org>
On Thu, Sep 25, 2014 at 3:46 AM, Andreas Dilger <adilger-m1MBpc4rdrD3fQ9qLvQP4Q@public.gmane.org> wrote:
> On Sep 24, 2014, at 14:15, Li Xi <pkuelelixi-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>
> Adds project quota support for ext4
>
> This patch adds mount options for enabling/disabling project quota
> accounting and enforcement. A new specific inode is also used for
> project quota accounting.
>
> Signed-off-by: Li Xi <lixi <at> ddn.com>
> Signed-off-by: Dmitry Monakhov <dmonakhov-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
> ---
> @@ -5084,7 +5155,18 @@ static int ext4_statfs(struct dentry *de
> buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL;
> buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL;
>
> - return 0;
> + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT) &&
> + ext4_test_inode_flag(inode, EXT4_INODE_PROJINHERIT) &&
>
>
> Does it make sense to check for the PROJINHERIT flag, or only if
> the inode has a non-zero project quota? I thought it is possible to
> have project quota on an inode that is not marked to be inherited?
This is trying to keep the same semantics with XFS. XFS returns
the project quota limits when statfs only if the inode has PROJINHERIT
flag. I think it is reasonable, since when a directory has PROJINHERIT,
the directory tree tend to has the same project ID.
xfs_fs_statfs():
...
if ((ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) &&
((mp->m_qflags & (XFS_PQUOTA_ACCT|XFS_PQUOTA_ENFD))) ==
(XFS_PQUOTA_ACCT|XFS_PQUOTA_ENFD))
xfs_qm_statvfs(ip, statp);
>
> + sb_has_quota_usage_enabled(sb, PRJQUOTA) &&
> + sb_has_quota_limits_enabled(sb, PRJQUOTA)) {
> + err = ext4_statfs_project(sb, EXT4_I(inode)->i_projid, buf);
> + if (err) {
> + ext4_warning(sb, "Cannot get quota of project %u\n",
>
>
> What is the benefit of this warning message? Would it be better to
> return the fs-wide statfs() data if the project quota is not found?
Yeah, that is exactly what statfs() is doing in current codes. statfs()
will first get the fs-wide data, and then try to get project data. Even
it fails to get project data, it will return successfully with fs-wide
data. Anyway, I will remove this message. :)
Regards,
Li Xi
^ permalink raw reply
* Re: [PATCH v4 0/4] quota: add project quota support
From: Li Xi @ 2014-09-24 23:42 UTC (permalink / raw)
To: Nikitas Angelinas
Cc: Jan Kara, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Ext4 Developers List, linux-api-u79uwXL29TY76Z2rM5mHXA,
Andreas Dilger,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org,
hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org, Dmitry Monakhov
In-Reply-To: <20140924222752.GA23810-xH2D3wQCr9zOfED8G0uUrv5vdDvN9gHfAL8bYrjMMd8@public.gmane.org>
On Thu, Sep 25, 2014 at 6:27 AM, Nikitas Angelinas
<nikitasangelinas-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Wed, Sep 24, 2014 at 11:42:39AM -0400, Theodore Ts'o wrote:
>> On Wed, Sep 24, 2014 at 02:52:06PM +0200, Jan Kara wrote:
>> > Whitespace got mangled in your patches so much they are hardly readable.
>> > Please resend them so that it is properly preserved. I'm not sure how other
>> > guys using gmail do this so you might need to ask them... Thanks!
>>
>> And it would be nice if they are chained together so that people with
>> proper mail-threading readers can easily find all of your patches.
>>
>> Probably the best way to do this is to configure your a local mail
>> transfer agent on your Linux desktop/laptop, and configure it to use
>> smart mail-router with authenticated SMTP to:
>>
>> Host: smtp.gmail.com
>> Port: 465
>> Authentication: Yes
>> Username: #YOU#@gmail.com
>> Password: #YOUR_PASSWORD#
>> Security: SSL
>>
>> Then use "git send-email" to post your patches, and it should all be
>> good.
>>
>> (I'm guessing your cutting and pasting your patches into the mail send
>> window, and that unfortunately tends not to work well.)
>>
>> Cheers,
>>
>> - Ted
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-api" in
>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Li Xi,
>
>
> You can set up Mutt (likely Alpine, maybe others) to work with GMail, if
> you want to; I believe it handles whitespace and patch series well by
> default. There are relevant resources online to assist with the
> configuration, if you need them. It also includes an SMTP feature, if you
> want to make use of it.
Hi Nikitas,
Thank you! I will try that too.
Regards,
Li Xi
^ permalink raw reply
* Re: [PATCH v4 0/4] quota: add project quota support
From: Li Xi @ 2014-09-24 23:41 UTC (permalink / raw)
To: Theodore Ts'o
Cc: Jan Kara, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Ext4 Developers List, linux-api-u79uwXL29TY76Z2rM5mHXA,
Andreas Dilger,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org,
hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org, Dmitry Monakhov
In-Reply-To: <20140924154239.GA11342-AKGzg7BKzIDYtjvyW6yDsg@public.gmane.org>
On Wed, Sep 24, 2014 at 11:42 PM, Theodore Ts'o <tytso-3s7WtUTddSA@public.gmane.org> wrote:
> On Wed, Sep 24, 2014 at 02:52:06PM +0200, Jan Kara wrote:
>> Whitespace got mangled in your patches so much they are hardly readable.
>> Please resend them so that it is properly preserved. I'm not sure how other
>> guys using gmail do this so you might need to ask them... Thanks!
>
> And it would be nice if they are chained together so that people with
> proper mail-threading readers can easily find all of your patches.
>
> Probably the best way to do this is to configure your a local mail
> transfer agent on your Linux desktop/laptop, and configure it to use
> smart mail-router with authenticated SMTP to:
>
> Host: smtp.gmail.com
> Port: 465
> Authentication: Yes
> Username: #YOU#@gmail.com
> Password: #YOUR_PASSWORD#
> Security: SSL
>
> Then use "git send-email" to post your patches, and it should all be
> good.
>
> (I'm guessing your cutting and pasting your patches into the mail send
> window, and that unfortunately tends not to work well.)
Thank you very much for the help. And sorry for the mistakes I've made.
That is too many times even for a newbie. Hopefully, I won't make any
mistake with 'git send-email' in the next version. :)
Regards,
Li Xi
^ permalink raw reply
* Re: [PATCH v4 0/4] quota: add project quota support
From: Nikitas Angelinas @ 2014-09-24 22:27 UTC (permalink / raw)
To: Li Xi
Cc: Jan Kara, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Ext4 Developers List, linux-api-u79uwXL29TY76Z2rM5mHXA,
Andreas Dilger,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org,
hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org, Dmitry Monakhov
In-Reply-To: <20140924154239.GA11342-AKGzg7BKzIDYtjvyW6yDsg@public.gmane.org>
On Wed, Sep 24, 2014 at 11:42:39AM -0400, Theodore Ts'o wrote:
> On Wed, Sep 24, 2014 at 02:52:06PM +0200, Jan Kara wrote:
> > Whitespace got mangled in your patches so much they are hardly readable.
> > Please resend them so that it is properly preserved. I'm not sure how other
> > guys using gmail do this so you might need to ask them... Thanks!
>
> And it would be nice if they are chained together so that people with
> proper mail-threading readers can easily find all of your patches.
>
> Probably the best way to do this is to configure your a local mail
> transfer agent on your Linux desktop/laptop, and configure it to use
> smart mail-router with authenticated SMTP to:
>
> Host: smtp.gmail.com
> Port: 465
> Authentication: Yes
> Username: #YOU#@gmail.com
> Password: #YOUR_PASSWORD#
> Security: SSL
>
> Then use "git send-email" to post your patches, and it should all be
> good.
>
> (I'm guessing your cutting and pasting your patches into the mail send
> window, and that unfortunately tends not to work well.)
>
> Cheers,
>
> - Ted
> --
> To unsubscribe from this list: send the line "unsubscribe linux-api" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Li Xi,
You can set up Mutt (likely Alpine, maybe others) to work with GMail, if
you want to; I believe it handles whitespace and patch series well by
default. There are relevant resources online to assist with the
configuration, if you need them. It also includes an SMTP feature, if you
want to make use of it.
Cheers,
Nikitas
^ permalink raw reply
* Re: [PATCH v2 1/7] selftests: add kselftest framework for uniform test reporting
From: Shuah Khan @ 2014-09-24 22:06 UTC (permalink / raw)
To: Davidlohr Bueso
Cc: akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
colin.king-Z7WLFzj8eWMS+FvcfC7Uqw,
ebiederm-aS9lmoZGLiVWk0Htik3J/w,
serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA,
thierry-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
luto-kltTT9wpgjJwATOyAt5JVQ, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Shuah Khan
In-Reply-To: <1411546144.30630.10.camel-dxKd5G12XOI1EaDjlw0dpg@public.gmane.org>
On 09/24/2014 02:09 AM, Davidlohr Bueso wrote:
>> +/* counters */
>> +static int ksft_pass;
>> +static int ksft_fail;
>> +static int ksft_xfail;
>> +static int ksft_xpass;
>> +static int ksft_xskip;
>
> unsigned int?
Yes unsigned int is a better choice.
>
>> +static inline void ksft_inc_pass_cnt(void) { ksft_pass++; }
>> +static inline void ksft_inc_fail_cnt(void) { ksft_fail++; }
>> +static inline void ksft_inc_xfail_cnt(void) { ksft_xfail++; }
>> +static inline void ksft_inc_xpass_cnt(void) { ksft_xpass++; }
>> +static inline void ksft_inc_xskip_cnt(void) { ksft_xskip++; }
>
> It would probably make sense to have the counters in a structures,
> something like: struct ksft_counter { ... } ksft_cnt;
>
> Then just pass it around the proposed functions as arguments. That also
> minimizes a bit the global variables and would allow you to easily
> change it in the future.
How does the following look?
struct ksft_count
{
unsigned int ksft_pass;
unsigned int ksft_fail;
unsigned int ksft_xfail;
unsigned int ksft_xpass;
unsigned int ksft_xskip;
};
static ksft_count ksft_cnt;
static inline void ksft_inc_pass_cnt(void) { ksft_cnt.ksft_pass++; }
static inline void ksft_inc_fail_cnt(void) { ksft_cnt.ksft_fail++; }
static inline void ksft_inc_xfail_cnt(void) { ksft_cnt.ksft_xfail++; }
static inline void ksft_inc_xpass_cnt(void) { ksft_cnt.ksft_xpass++; }
static inline void ksft_inc_xskip_cnt(void) { ksft_cnt.ksft_xskip++; }
With this approach, tests don't have to define their own counter
variable and pass it in. I am looking to abstract the framework
as much as possible.
thanks,
-- Shuah
--
Shuah Khan
Sr. Linux Kernel Developer
Samsung Research America (Silicon Valley)
shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org | (970) 217-8978
^ permalink raw reply
* [PATCH v4 20/23] amdkfd: Implement the create/destroy/update queue IOCTLs
From: Oded Gabbay @ 2014-09-24 20:45 UTC (permalink / raw)
To: David Airlie, Alex Deucher, Jerome Glisse
Cc: Andrew Lewycky, linux-kernel, dri-devel, linux-api,
Christian König
In-Reply-To: <1411591537-31636-1-git-send-email-oded.gabbay@amd.com>
From: Ben Goz <ben.goz@amd.com>
v3: Removed the use of internal typedefs, fixed debug prints, added checks
for parameters and moved to using doorbell address from user
v4: Extracted some of the code in the create queue ioctl to a different
function that may be also called from other ioctls in the future. Also fixed
the check of the ring size argument.
Signed-off-by: Ben Goz <ben.goz@amd.com>
Signed-off-by: Oded Gabbay <oded.gabbay@amd.com>
---
drivers/gpu/drm/radeon/amdkfd/kfd_chardev.c | 191 ++++++++++++++++++++-
drivers/gpu/drm/radeon/amdkfd/kfd_priv.h | 8 +
.../drm/radeon/amdkfd/kfd_process_queue_manager.c | 5 +-
include/uapi/linux/kfd_ioctl.h | 7 +-
4 files changed, 205 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/radeon/amdkfd/kfd_chardev.c b/drivers/gpu/drm/radeon/amdkfd/kfd_chardev.c
index c42f53b..ac3146d 100644
--- a/drivers/gpu/drm/radeon/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/radeon/amdkfd/kfd_chardev.c
@@ -117,19 +117,204 @@ static int kfd_open(struct inode *inode, struct file *filep)
return 0;
}
+static int set_queue_properties_from_user(struct queue_properties *q_properties,
+ struct kfd_ioctl_create_queue_args *args)
+{
+ if (args->queue_percentage > KFD_MAX_QUEUE_PERCENTAGE) {
+ pr_err("kfd: queue percentage must be between 0 to KFD_MAX_QUEUE_PERCENTAGE\n");
+ return -EINVAL;
+ }
+
+ if (args->queue_priority > KFD_MAX_QUEUE_PRIORITY) {
+ pr_err("kfd: queue priority must be between 0 to KFD_MAX_QUEUE_PRIORITY\n");
+ return -EINVAL;
+ }
+
+ if ((args->ring_base_address) &&
+ (!access_ok(VERIFY_WRITE, args->ring_base_address, sizeof(uint64_t)))) {
+ pr_err("kfd: can't access ring base address\n");
+ return -EFAULT;
+ }
+
+ if (!is_power_of_2(args->ring_size) && (args->ring_size != 0)) {
+ pr_err("kfd: ring size must be a power of 2 or 0\n");
+ return -EINVAL;
+ }
+
+ if (!access_ok(VERIFY_WRITE, args->read_pointer_address, sizeof(uint32_t))) {
+ pr_err("kfd: can't access read pointer\n");
+ return -EFAULT;
+ }
+
+ if (!access_ok(VERIFY_WRITE, args->write_pointer_address, sizeof(uint32_t))) {
+ pr_err("kfd: can't access write pointer\n");
+ return -EFAULT;
+ }
+
+ q_properties->is_interop = false;
+ q_properties->queue_percent = args->queue_percentage;
+ q_properties->priority = args->queue_priority;
+ q_properties->queue_address = args->ring_base_address;
+ q_properties->queue_size = args->ring_size;
+ q_properties->read_ptr = (uint32_t *) args->read_pointer_address;
+ q_properties->write_ptr = (uint32_t *) args->write_pointer_address;
+
+ pr_debug("Queue Percentage (%d, %d)\n",
+ q_properties->queue_percent, args->queue_percentage);
+
+ pr_debug("Queue Priority (%d, %d)\n",
+ q_properties->priority, args->queue_priority);
+
+ pr_debug("Queue Address (0x%llX, 0x%llX)\n",
+ q_properties->queue_address, args->ring_base_address);
+
+ pr_debug("Queue Size (0x%llX, %u)\n",
+ q_properties->queue_size, args->ring_size);
+
+ pr_debug("Queue r/w Pointers (0x%llX, 0x%llX)\n",
+ (uint64_t) q_properties->read_ptr,
+ (uint64_t) q_properties->write_ptr);
+
+ return 0;
+}
+
static long kfd_ioctl_create_queue(struct file *filep, struct kfd_process *p, void __user *arg)
{
- return -ENODEV;
+ struct kfd_ioctl_create_queue_args args;
+ struct kfd_dev *dev;
+ int err = 0;
+ unsigned int queue_id;
+ struct kfd_process_device *pdd;
+ struct queue_properties q_properties;
+
+ memset(&q_properties, 0, sizeof(struct queue_properties));
+
+ if (copy_from_user(&args, arg, sizeof(args)))
+ return -EFAULT;
+
+ pr_debug("kfd: creating queue ioctl\n");
+
+ err = set_queue_properties_from_user(&q_properties, &args);
+ if (err)
+ return err;
+
+ dev = kfd_device_by_id(args.gpu_id);
+ if (dev == NULL)
+ return -EINVAL;
+
+ mutex_lock(&p->mutex);
+
+ pdd = kfd_bind_process_to_device(dev, p);
+ if (IS_ERR(pdd) < 0) {
+ err = PTR_ERR(pdd);
+ goto err_bind_process;
+ }
+
+ pr_debug("kfd: creating queue for PASID %d on GPU 0x%x\n",
+ p->pasid,
+ dev->id);
+
+ err = pqm_create_queue(&p->pqm, dev, filep, &q_properties, 0, KFD_QUEUE_TYPE_COMPUTE, &queue_id);
+ if (err != 0)
+ goto err_create_queue;
+
+ args.queue_id = queue_id;
+
+ /* Return gpu_id as doorbell offset for mmap usage */
+ args.doorbell_offset = args.gpu_id << PAGE_SHIFT;
+
+ if (copy_to_user(arg, &args, sizeof(args))) {
+ err = -EFAULT;
+ goto err_copy_args_out;
+ }
+
+ mutex_unlock(&p->mutex);
+
+ pr_debug("kfd: queue id %d was created successfully\n", args.queue_id);
+
+ pr_debug("ring buffer address == 0x%016llX\n",
+ args.ring_base_address);
+
+ pr_debug("read ptr address == 0x%016llX\n",
+ args.read_pointer_address);
+
+ pr_debug("write ptr address == 0x%016llX\n",
+ args.write_pointer_address);
+
+ return 0;
+
+err_copy_args_out:
+ pqm_destroy_queue(&p->pqm, queue_id);
+err_create_queue:
+err_bind_process:
+ mutex_unlock(&p->mutex);
+ return err;
}
static int kfd_ioctl_destroy_queue(struct file *filp, struct kfd_process *p, void __user *arg)
{
- return -ENODEV;
+ int retval;
+ struct kfd_ioctl_destroy_queue_args args;
+
+ if (copy_from_user(&args, arg, sizeof(args)))
+ return -EFAULT;
+
+ pr_debug("kfd: destroying queue id %d for PASID %d\n",
+ args.queue_id,
+ p->pasid);
+
+ mutex_lock(&p->mutex);
+
+ retval = pqm_destroy_queue(&p->pqm, args.queue_id);
+
+ mutex_unlock(&p->mutex);
+ return retval;
}
static int kfd_ioctl_update_queue(struct file *filp, struct kfd_process *p, void __user *arg)
{
- return -ENODEV;
+ int retval;
+ struct kfd_ioctl_update_queue_args args;
+ struct queue_properties properties;
+
+ if (copy_from_user(&args, arg, sizeof(args)))
+ return -EFAULT;
+
+ if (args.queue_percentage > KFD_MAX_QUEUE_PERCENTAGE) {
+ pr_err("kfd: queue percentage must be between 0 to KFD_MAX_QUEUE_PERCENTAGE\n");
+ return -EINVAL;
+ }
+
+ if (args.queue_priority > KFD_MAX_QUEUE_PRIORITY) {
+ pr_err("kfd: queue priority must be between 0 to KFD_MAX_QUEUE_PRIORITY\n");
+ return -EINVAL;
+ }
+
+ if ((args.ring_base_address) &&
+ (!access_ok(VERIFY_WRITE, args.ring_base_address, sizeof(uint64_t)))) {
+ pr_err("kfd: can't access ring base address\n");
+ return -EFAULT;
+ }
+
+ if (!is_power_of_2(args.ring_size) && (args.ring_size != 0)) {
+ pr_err("kfd: ring size must be a power of 2 or 0\n");
+ return -EINVAL;
+ }
+
+ properties.queue_address = args.ring_base_address;
+ properties.queue_size = args.ring_size;
+ properties.queue_percent = args.queue_percentage;
+ properties.priority = args.queue_priority;
+
+ pr_debug("kfd: updating queue id %d for PASID %d\n", args.queue_id, p->pasid);
+
+ mutex_lock(&p->mutex);
+
+ retval = pqm_update_queue(&p->pqm, args.queue_id, &properties);
+
+ mutex_unlock(&p->mutex);
+
+ return retval;
}
static long kfd_ioctl_set_memory_policy(struct file *filep, struct kfd_process *p, void __user *arg)
diff --git a/drivers/gpu/drm/radeon/amdkfd/kfd_priv.h b/drivers/gpu/drm/radeon/amdkfd/kfd_priv.h
index 6c656d8..0e3e18f 100644
--- a/drivers/gpu/drm/radeon/amdkfd/kfd_priv.h
+++ b/drivers/gpu/drm/radeon/amdkfd/kfd_priv.h
@@ -513,7 +513,15 @@ struct process_queue_node {
int pqm_init(struct process_queue_manager *pqm, struct kfd_process *p);
void pqm_uninit(struct process_queue_manager *pqm);
+int pqm_create_queue(struct process_queue_manager *pqm,
+ struct kfd_dev *dev,
+ struct file *f,
+ struct queue_properties *properties,
+ unsigned int flags,
+ enum kfd_queue_type type,
+ unsigned int *qid);
int pqm_destroy_queue(struct process_queue_manager *pqm, unsigned int qid);
+int pqm_update_queue(struct process_queue_manager *pqm, unsigned int qid, struct queue_properties *p);
/* Packet Manager */
diff --git a/drivers/gpu/drm/radeon/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/radeon/amdkfd/kfd_process_queue_manager.c
index 5bd6a5d..780c749 100644
--- a/drivers/gpu/drm/radeon/amdkfd/kfd_process_queue_manager.c
+++ b/drivers/gpu/drm/radeon/amdkfd/kfd_process_queue_manager.c
@@ -264,7 +264,10 @@ int pqm_destroy_queue(struct process_queue_manager *pqm, unsigned int qid)
pr_debug("kfd: In Func %s\n", __func__);
pqn = get_queue_by_qid(pqm, qid);
- BUG_ON(!pqn);
+ if (pqn == NULL) {
+ pr_err("kfd: queue id does not match any known queue\n");
+ return -EINVAL;
+ }
dev = NULL;
if (pqn->kq)
diff --git a/include/uapi/linux/kfd_ioctl.h b/include/uapi/linux/kfd_ioctl.h
index a06e021..1ce8302 100644
--- a/include/uapi/linux/kfd_ioctl.h
+++ b/include/uapi/linux/kfd_ioctl.h
@@ -34,8 +34,11 @@ struct kfd_ioctl_get_version_args {
};
/* For kfd_ioctl_create_queue_args.queue_type. */
-#define KFD_IOC_QUEUE_TYPE_COMPUTE 0
-#define KFD_IOC_QUEUE_TYPE_SDMA 1
+#define KFD_IOC_QUEUE_TYPE_COMPUTE 0
+#define KFD_IOC_QUEUE_TYPE_SDMA 1
+
+#define KFD_MAX_QUEUE_PERCENTAGE 100
+#define KFD_MAX_QUEUE_PRIORITY 15
struct kfd_ioctl_create_queue_args {
uint64_t ring_base_address; /* to KFD */
--
1.9.1
^ permalink raw reply related
* [PATCH v4 07/23] amdkfd: Add IOCTL set definitions of amdkfd
From: Oded Gabbay @ 2014-09-24 20:45 UTC (permalink / raw)
To: David Airlie, Alex Deucher, Jerome Glisse
Cc: Andrew Lewycky, linux-kernel, dri-devel, linux-api
In-Reply-To: <1411591537-31636-1-git-send-email-oded.gabbay@amd.com>
- KFD_IOC_GET_VERSION:
Retrieves the interface version of amdkfd
- KFD_IOC_CREATE_QUEUE:
Creates a usermode queue that runs on a specific GPU device
- KFD_IOC_DESTROY_QUEUE:
Destroys an existing usermode queue
- KFD_IOC_SET_MEMORY_POLICY:
Sets the memory policy of the default and alternate aperture of the
calling process
- KFD_IOC_GET_CLOCK_COUNTERS:
Retrieves counters (timestamps) of CPU and GPU
- KFD_IOC_GET_PROCESS_APERTURES:
Retrieves information about process apertures that were initialized
during the open() call of the amdkfd device
- KFD_IOC_UPDATE_QUEUE:
Updates configuration of an existing usermode queue
v3: Remove pragma pack and pmc ioctls. Added parameter for doorbell offset and
a comment on counters
Signed-off-by: Oded Gabbay <oded.gabbay@amd.com>
---
include/uapi/linux/kfd_ioctl.h | 123 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 123 insertions(+)
create mode 100644 include/uapi/linux/kfd_ioctl.h
diff --git a/include/uapi/linux/kfd_ioctl.h b/include/uapi/linux/kfd_ioctl.h
new file mode 100644
index 0000000..a06e021
--- /dev/null
+++ b/include/uapi/linux/kfd_ioctl.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2014 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef KFD_IOCTL_H_INCLUDED
+#define KFD_IOCTL_H_INCLUDED
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define KFD_IOCTL_CURRENT_VERSION 1
+
+struct kfd_ioctl_get_version_args {
+ uint32_t min_supported_version; /* from KFD */
+ uint32_t max_supported_version; /* from KFD */
+};
+
+/* For kfd_ioctl_create_queue_args.queue_type. */
+#define KFD_IOC_QUEUE_TYPE_COMPUTE 0
+#define KFD_IOC_QUEUE_TYPE_SDMA 1
+
+struct kfd_ioctl_create_queue_args {
+ uint64_t ring_base_address; /* to KFD */
+ uint64_t write_pointer_address; /* from KFD */
+ uint64_t read_pointer_address; /* from KFD */
+ uint64_t doorbell_offset; /* from KFD */
+
+ uint32_t ring_size; /* to KFD */
+ uint32_t gpu_id; /* to KFD */
+ uint32_t queue_type; /* to KFD */
+ uint32_t queue_percentage; /* to KFD */
+ uint32_t queue_priority; /* to KFD */
+ uint32_t queue_id; /* from KFD */
+};
+
+struct kfd_ioctl_destroy_queue_args {
+ uint32_t queue_id; /* to KFD */
+};
+
+struct kfd_ioctl_update_queue_args {
+ uint64_t ring_base_address; /* to KFD */
+
+ uint32_t queue_id; /* to KFD */
+ uint32_t ring_size; /* to KFD */
+ uint32_t queue_percentage; /* to KFD */
+ uint32_t queue_priority; /* to KFD */
+};
+
+/* For kfd_ioctl_set_memory_policy_args.default_policy and alternate_policy */
+#define KFD_IOC_CACHE_POLICY_COHERENT 0
+#define KFD_IOC_CACHE_POLICY_NONCOHERENT 1
+
+struct kfd_ioctl_set_memory_policy_args {
+ uint64_t alternate_aperture_base; /* to KFD */
+ uint64_t alternate_aperture_size; /* to KFD */
+
+ uint32_t gpu_id; /* to KFD */
+ uint32_t default_policy; /* to KFD */
+ uint32_t alternate_policy; /* to KFD */
+};
+
+/*
+ * All counters are monotonic. They are used for profiling of compute jobs.
+ * The profiling is done by userspace.
+ *
+ * In case of GPU reset, the counter should not be affected.
+ */
+
+struct kfd_ioctl_get_clock_counters_args {
+ uint64_t gpu_clock_counter; /* from KFD */
+ uint64_t cpu_clock_counter; /* from KFD */
+ uint64_t system_clock_counter; /* from KFD */
+ uint64_t system_clock_freq; /* from KFD */
+
+ uint32_t gpu_id; /* to KFD */
+};
+
+#define NUM_OF_SUPPORTED_GPUS 7
+
+struct kfd_process_device_apertures {
+ uint64_t lds_base; /* from KFD */
+ uint64_t lds_limit; /* from KFD */
+ uint64_t scratch_base; /* from KFD */
+ uint64_t scratch_limit; /* from KFD */
+ uint64_t gpuvm_base; /* from KFD */
+ uint64_t gpuvm_limit; /* from KFD */
+ uint32_t gpu_id; /* from KFD */
+};
+
+struct kfd_ioctl_get_process_apertures_args {
+ struct kfd_process_device_apertures process_apertures[NUM_OF_SUPPORTED_GPUS];/* from KFD */
+ uint8_t num_of_nodes; /* from KFD, should be in the range [1 - NUM_OF_SUPPORTED_GPUS]*/
+};
+
+#define KFD_IOC_MAGIC 'K'
+
+#define KFD_IOC_GET_VERSION _IOR(KFD_IOC_MAGIC, 1, struct kfd_ioctl_get_version_args)
+#define KFD_IOC_CREATE_QUEUE _IOWR(KFD_IOC_MAGIC, 2, struct kfd_ioctl_create_queue_args)
+#define KFD_IOC_DESTROY_QUEUE _IOWR(KFD_IOC_MAGIC, 3, struct kfd_ioctl_destroy_queue_args)
+#define KFD_IOC_SET_MEMORY_POLICY _IOW(KFD_IOC_MAGIC, 4, struct kfd_ioctl_set_memory_policy_args)
+#define KFD_IOC_GET_CLOCK_COUNTERS _IOWR(KFD_IOC_MAGIC, 5, struct kfd_ioctl_get_clock_counters_args)
+#define KFD_IOC_GET_PROCESS_APERTURES _IOR(KFD_IOC_MAGIC, 6, struct kfd_ioctl_get_process_apertures_args)
+#define KFD_IOC_UPDATE_QUEUE _IOW(KFD_IOC_MAGIC, 7, struct kfd_ioctl_update_queue_args)
+
+#endif
--
1.9.1
^ permalink raw reply related
* [PATCH v3 3/3] tools: parallel selftests building & running
From: Peter Feiner @ 2014-09-24 19:42 UTC (permalink / raw)
To: Shuah Khan
Cc: linux-api, linux-kernel, David Herrmann, Greg Thelen,
Hugh Dickens, Andrew Morton, Peter Feiner
In-Reply-To: <1411587731-25088-1-git-send-email-pfeiner@google.com>
Now make -jN builds and runs selftests in parallel. Also, if one
selftest fails to build or run, make will return an error, whereas
before the error was ignored.
Signed-off-by: Peter Feiner <pfeiner@google.com>
---
v1 -> v2:
Moved fix for missing 'make clean' target into separate
patch.
---
tools/testing/selftests/Makefile | 49 ++++++++++++++++------------------------
1 file changed, 20 insertions(+), 29 deletions(-)
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 36ff2e4..8c33716 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -18,32 +18,23 @@ TARGETS += firmware
TARGETS_HOTPLUG = cpu-hotplug
TARGETS_HOTPLUG += memory-hotplug
-all:
- for TARGET in $(TARGETS); do \
- make -C $$TARGET; \
- done;
-
-run_tests: all
- for TARGET in $(TARGETS); do \
- make -C $$TARGET run_tests; \
- done;
-
-hotplug:
- for TARGET in $(TARGETS_HOTPLUG); do \
- make -C $$TARGET; \
- done;
-
-run_hotplug: hotplug
- for TARGET in $(TARGETS_HOTPLUG); do \
- make -C $$TARGET run_full_test; \
- done;
-
-clean_hotplug:
- for TARGET in $(TARGETS_HOTPLUG); do \
- make -C $$TARGET clean; \
- done;
-
-clean:
- for TARGET in $(TARGETS); do \
- make -C $$TARGET clean; \
- done;
+BUILD_TARGETS=$(TARGETS:%=build-%) $(TARGETS_HOTPLUG:%=build-%)
+TEST_TARGETS=$(TARGETS:%=test-%)
+CLEAN_TARGETS=$(TARGETS:%=clean-%) $(TARGETS_HOTPLUG:%=clean-%)
+
+all: $(BUILD_TARGETS)
+
+run_hotplug: test-cpu-hotplug test-memory-hotplug
+
+run_tests: $(TEST_TARGETS)
+
+clean: $(CLEAN_TARGETS)
+
+build-%:
+ $(MAKE) -C $(@:build-%=%)
+
+test-%: build-%
+ $(MAKE) -C $(@:test-%=%) run_tests
+
+clean-%:
+ $(MAKE) -C $(@:clean-%=%) clean
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
* [PATCH v3 2/3] tools: adding clean target to user selftest
From: Peter Feiner @ 2014-09-24 19:42 UTC (permalink / raw)
To: Shuah Khan
Cc: linux-api, linux-kernel, David Herrmann, Greg Thelen,
Hugh Dickens, Andrew Morton, Peter Feiner
In-Reply-To: <1411587731-25088-1-git-send-email-pfeiner@google.com>
Gets rid of this error when running 'make clean' in the selftests
directory:
make[1]: Entering directory `.../tools/testing/selftests/user'
make[1]: *** No rule to make target `clean'. Stop.
Signed-off-by: Peter Feiner <pfeiner@google.com>
---
v1 -> v2:
Separated this from the parallel build patch.
v2 -> v3:
Added Signed-off-by.
---
tools/testing/selftests/user/Makefile | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tools/testing/selftests/user/Makefile b/tools/testing/selftests/user/Makefile
index 396255b..8eb6583 100644
--- a/tools/testing/selftests/user/Makefile
+++ b/tools/testing/selftests/user/Makefile
@@ -3,6 +3,8 @@
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
all:
+clean:
+
run_tests: all
@if /sbin/modprobe test_user_copy ; then \
rmmod test_user_copy; \
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
* [PATCH v3 1/3] tools: add .gitignore entries for selftests
From: Peter Feiner @ 2014-09-24 19:42 UTC (permalink / raw)
To: Shuah Khan
Cc: linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, David Herrmann, Greg Thelen,
Hugh Dickens, Andrew Morton, Peter Feiner
In-Reply-To: <1411587731-25088-1-git-send-email-pfeiner-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Ignore the binaries built for selftests in 'git status' output to make
development more pleasant. Without this patch, you see
tools/testing/selftests/breakpoints/breakpoint_test
tools/testing/selftests/efivarfs/create-read
tools/testing/selftests/efivarfs/open-unlink
tools/testing/selftests/kcmp/kcmp_test
tools/testing/selftests/memfd/memfd_test
tools/testing/selftests/mqueue/mq_open_tests
tools/testing/selftests/mqueue/mq_perf_tests
tools/testing/selftests/net/psock_fanout
tools/testing/selftests/net/psock_tpacket
tools/testing/selftests/net/socket
tools/testing/selftests/ptrace/peeksiginfo
tools/testing/selftests/timers/posix_timers
tools/testing/selftests/vm/hugepage-mmap
tools/testing/selftests/vm/hugepage-shm
tools/testing/selftests/vm/hugetlbfstest
tools/testing/selftests/vm/map_hugetlb
tools/testing/selftests/vm/thuge-gen
tools/testing/selftests/mount/unprivileged-remount-test
in the list of untracked files.
Signed-off-by: Peter Feiner <pfeiner-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
---
v1 -> v2:
* added changelog blurb
* added mount/.gitignore for unprivileged-remount-test
---
tools/testing/selftests/breakpoints/.gitignore | 1 +
tools/testing/selftests/efivarfs/.gitignore | 2 ++
tools/testing/selftests/mount/.gitignore | 1 +
tools/testing/selftests/ptrace/.gitignore | 1 +
tools/testing/selftests/timers/.gitignore | 1 +
tools/testing/selftests/vm/.gitignore | 9 +++++----
6 files changed, 11 insertions(+), 4 deletions(-)
create mode 100644 tools/testing/selftests/breakpoints/.gitignore
create mode 100644 tools/testing/selftests/efivarfs/.gitignore
create mode 100644 tools/testing/selftests/mount/.gitignore
create mode 100644 tools/testing/selftests/ptrace/.gitignore
create mode 100644 tools/testing/selftests/timers/.gitignore
diff --git a/tools/testing/selftests/breakpoints/.gitignore b/tools/testing/selftests/breakpoints/.gitignore
new file mode 100644
index 0000000..ad66922
--- /dev/null
+++ b/tools/testing/selftests/breakpoints/.gitignore
@@ -0,0 +1 @@
+/breakpoint_test
diff --git a/tools/testing/selftests/efivarfs/.gitignore b/tools/testing/selftests/efivarfs/.gitignore
new file mode 100644
index 0000000..66c6e85
--- /dev/null
+++ b/tools/testing/selftests/efivarfs/.gitignore
@@ -0,0 +1,2 @@
+/create-read
+/open-unlink
diff --git a/tools/testing/selftests/mount/.gitignore b/tools/testing/selftests/mount/.gitignore
new file mode 100644
index 0000000..7ffb29f
--- /dev/null
+++ b/tools/testing/selftests/mount/.gitignore
@@ -0,0 +1 @@
+/unprivileged-remount-test
diff --git a/tools/testing/selftests/ptrace/.gitignore b/tools/testing/selftests/ptrace/.gitignore
new file mode 100644
index 0000000..d348b86
--- /dev/null
+++ b/tools/testing/selftests/ptrace/.gitignore
@@ -0,0 +1 @@
+/peeksiginfo
diff --git a/tools/testing/selftests/timers/.gitignore b/tools/testing/selftests/timers/.gitignore
new file mode 100644
index 0000000..ac85e5b
--- /dev/null
+++ b/tools/testing/selftests/timers/.gitignore
@@ -0,0 +1 @@
+/posix_timers
diff --git a/tools/testing/selftests/vm/.gitignore b/tools/testing/selftests/vm/.gitignore
index ff1bb16..e57961f 100644
--- a/tools/testing/selftests/vm/.gitignore
+++ b/tools/testing/selftests/vm/.gitignore
@@ -1,4 +1,5 @@
-hugepage-mmap
-hugepage-shm
-map_hugetlb
-thuge-gen
+/hugepage-mmap
+/hugepage-shm
+/map_hugetlb
+/thuge-gen
+/hugetlbfstest
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
* [PATCH v2 0/3] tools: selftests cleanup
From: Peter Feiner @ 2014-09-24 19:42 UTC (permalink / raw)
To: Shuah Khan
Cc: linux-api, linux-kernel, David Herrmann, Greg Thelen,
Hugh Dickens, Andrew Morton, Peter Feiner
In-Reply-To: <1411499027-17801-1-git-send-email-pfeiner@google.com>
A couple of small patches to make working with selftests easier.
v1 -> v2:
Addressed Shuah's comments.
v2 -> v3:
Forgot Signed-off-by footer.
*** BLURB HERE ***
Peter Feiner (3):
tools: add .gitignore entries for selftests
tools: adding clean target to user selftest
tools: parallel selftests building & running
tools/testing/selftests/Makefile | 49 +++++++++++---------------
tools/testing/selftests/breakpoints/.gitignore | 1 +
tools/testing/selftests/efivarfs/.gitignore | 2 ++
tools/testing/selftests/mount/.gitignore | 1 +
tools/testing/selftests/ptrace/.gitignore | 1 +
tools/testing/selftests/timers/.gitignore | 1 +
tools/testing/selftests/user/Makefile | 2 ++
tools/testing/selftests/vm/.gitignore | 9 ++---
8 files changed, 33 insertions(+), 33 deletions(-)
create mode 100644 tools/testing/selftests/breakpoints/.gitignore
create mode 100644 tools/testing/selftests/efivarfs/.gitignore
create mode 100644 tools/testing/selftests/mount/.gitignore
create mode 100644 tools/testing/selftests/ptrace/.gitignore
create mode 100644 tools/testing/selftests/timers/.gitignore
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply
* Re: [PATCH 1/4] Adds general codes to enforces project quota limits
From: Jan Kara @ 2014-09-24 17:37 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Jan Kara, Li Xi, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
linux-ext4-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
adilger-m1MBpc4rdrD3fQ9qLvQP4Q,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn,
dmonakhov-GEFAQzZX7r8dnm+yROfE0A
In-Reply-To: <20140924171306.GA23874-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>
On Wed 24-09-14 10:13:06, Christoph Hellwig wrote:
> On Wed, Sep 24, 2014 at 07:10:20PM +0200, Jan Kara wrote:
> > Yeah, I'm aware of that but I decided I won't bother Li Xi with that
> > since it's independent issue and 8 bytes aren't that terrible.
>
> For the inode of which we have so many instances it is. Especially when
> only one filesystem can make use of this new field.
>
> > Personally
> > I somewhat prefer what I did in
> > http://www.spinics.net/lists/linux-fsdevel/msg74927.html
> > where we don't introduce additional method but rather a table with field
> > offsets in superblock. If people agree with this, I can cook up a patch for
> > quota relatively quickly.
>
> There's an even better way. We stopped calling the dquot_* functions
> from generic code, so now the filesystem can simply pass in a pointer
> to the dquot array.
I thought that as well before I tried that :) There are situations like
when turning quotas on & off where we iterate over all inodes and need to
get dquots from the inode... I don't see how to handle those situations
without fs callback / table of offsets.
Honza
--
Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
SUSE Labs, CR
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox