Linux userland API discussions
 help / color / mirror / Atom feed
* Re: [Xen-devel] [PATCH 1/2] xen: Implement ioctl to restrict privcmd to a specific domain
From: Frediano Ziglio @ 2014-08-01 13:44 UTC (permalink / raw)
  To: Jan Beulich
  Cc: David Vrabel, xen-devel, Boris Ostrovsky, Konrad Rzeszutek Wilk,
	linux-api-u79uwXL29TY76Z2rM5mHXA, linux-kernel
In-Reply-To: <53DB6B8D02000078000284D3-tRfBTM6QL9aeHWOVceGJHFaTQe2KTcn/@public.gmane.org>

On Fri, 2014-08-01 at 09:27 +0100, Jan Beulich wrote:
> >>> On 31.07.14 at 15:16, <frediano.ziglio-Sxgqhf6Nn4DQT0dZR+AlfA@public.gmane.org> wrote:
> > Add a RESTRICT ioctl to /dev/xen/privcmd, which allows privileged commands
> > file descriptor to be restricted to only working with a particular domain.
> 
> The "with" here has been quite confusing, and I realized that you
> mean the subject domain rather than the actor one only after
> having gone through quite some parts of the patch. For a patch
> this size, a little more of a description (and the original motivation)
> would have helped.
> 

Yes, you are right.

> Wrt motivation: Why does this need enforcing in the kernel at all?
> Doesn't XSM_DM_PRIV mode deal specifically with what you're
> trying to do here? Or else I guess I really need some better
> explanation of what this is about.
> 
> Jan
> 

This is quite old for me but you are right, perhaps is not that clear
for other people. In XenServer we have some patches that allow Qemu
running in dom0 but work only for a specific domain. The patches
required changes to libxc, kernel and Qemu. We are reimplementing these
patches as the old implementation has some problems (one is that the
patch for libxc was quite big). This feature was removed as kernel
patches did not work with newer (3.x) kernels.

Now, XSM_DM_PRIV works checking if the domain target is the domain we
are going to handle. However if your dom0 (as in XenServer) has all Qemu
to handle all VMs it cannot be bound to a single target so XSM is not
usable. Xen has no knowledge of process or file descriptor (which are
kernel specific) so there is actually no way it can distinguish which
domain should be restricted to. It would solve if the restriction would
be done for system call (so we can say execute this hypercall(s) with
these policies). However this require to change the target to be at
least CPU specific and handle preemption correctly in order to not mix
policies. This could be quite heavy so we hack the kernel in order to do
the restriction instead (it also was easier to port the patches).

Actually changes in Qemu to handle the privcmd/evtchn restrictions are
quite small, mainly restrict these two handles with an ioctl. Other
parts of the patch (chroot, setuid, groups, resource limits, and mostly
xenstore accesses) are more heavy.

Frediano

^ permalink raw reply

* Re: [PATCH 1/5] Add __designated_init, wrapping __attribute__((designated_init))
From: Andrew Morton @ 2014-08-01 20:45 UTC (permalink / raw)
  To: Josh Triplett
  Cc: J. Bruce Fields, Alexander Viro, Christopher Li, Ingo Molnar,
	Jeff Layton, Michal Marek, Neil Brown, Steven Rostedt,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
	linux-kbuild-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-raid-u79uwXL29TY76Z2rM5mHXA,
	linux-sparse-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <3130b0553b15518e3bef6d14c80280beed0f5ff9.1406850006.git.josh-iaAMLnmF4UmaiuxdJuQwMA@public.gmane.org>

On Thu, 31 Jul 2014 16:47:23 -0700 Josh Triplett <josh-iaAMLnmF4UmaiuxdJuQwMA@public.gmane.org> wrote:

> GCC 4.10 and newer, and Sparse, supports
> __attribute__((designated_init)), which marks a structure as requiring
> a designated initializer rather than a positional one.  This helps
> reduce churn and errors when used with _ops structures and similar
> structures designed for future extension.
> 
> Add a wrapper __designated_init, which turns into
> __attribute__((designated_init)) for Sparse or sufficiently new GCC.
> Enable the corresponding warning as an error.
> 
> The following semantic patch can help mark structures containing
> function pointers as requiring designated initializers:
> 
> @@
> identifier I, f;
> type T;
> @@
> 
> struct I {
> 	...
> 	T (*f)(...);
> 	...
> }
> + __designated_init

hm, dunno about this.

I think that the kernel should always use designated initializers
everywhere.  Perhaps there are a few special cases where positional
initializers provide a superior result but I'm not sure where those
might be.

In which case what we should do is to teach sparse to warn about
positional initializers then go fix them all up (lol).  After that
process is complete, this __designated_init tag would be just noise.

To support this perhaps a sparse tag would be needed which says
"positional initializers are OK here".  This way we're adding the
annotation to the exceptional cases, not to the common cases.

^ permalink raw reply

* Re: [PATCH 1/5] Add __designated_init, wrapping __attribute__((designated_init))
From: josh @ 2014-08-01 21:36 UTC (permalink / raw)
  To: Andrew Morton
  Cc: J. Bruce Fields, Alexander Viro, Christopher Li, Ingo Molnar,
	Jeff Layton, Michal Marek, Neil Brown, Steven Rostedt, linux-api,
	linux-fsdevel, linux-kbuild, linux-kernel, linux-raid,
	linux-sparse
In-Reply-To: <20140801134529.40b46e92a48d9bf0c0abc139@linux-foundation.org>

On Fri, Aug 01, 2014 at 01:45:29PM -0700, Andrew Morton wrote:
> On Thu, 31 Jul 2014 16:47:23 -0700 Josh Triplett <josh@joshtriplett.org> wrote:
> 
> > GCC 4.10 and newer, and Sparse, supports
> > __attribute__((designated_init)), which marks a structure as requiring
> > a designated initializer rather than a positional one.  This helps
> > reduce churn and errors when used with _ops structures and similar
> > structures designed for future extension.
> > 
> > Add a wrapper __designated_init, which turns into
> > __attribute__((designated_init)) for Sparse or sufficiently new GCC.
> > Enable the corresponding warning as an error.
> > 
> > The following semantic patch can help mark structures containing
> > function pointers as requiring designated initializers:
> > 
> > @@
> > identifier I, f;
> > type T;
> > @@
> > 
> > struct I {
> > 	...
> > 	T (*f)(...);
> > 	...
> > }
> > + __designated_init
> 
> hm, dunno about this.
> 
> I think that the kernel should always use designated initializers
> everywhere.  Perhaps there are a few special cases where positional
> initializers provide a superior result but I'm not sure where those
> might be.
> 
> In which case what we should do is to teach sparse to warn about
> positional initializers then go fix them all up (lol).  After that
> process is complete, this __designated_init tag would be just noise.
> 
> To support this perhaps a sparse tag would be needed which says
> "positional initializers are OK here".  This way we're adding the
> annotation to the exceptional cases, not to the common cases.

Before submitting this series, I actually used coccinelle to
automatically add __designated_init to nearly a thousand structures
(just in include/linux/ alone), and did a build with that.  Even just
adding it to structures with function pointers, some cases produce a
*huge* number of warnings.  I think it might make sense to work our way
through those warnings, but "go fix them all up" would be a gargantuan
effort.  (For an idea of scale: there were more warnings about
positional initialization than everything other warning combined, by a
substantial factor, and that's just from changing a few hundred
structures.)

I'm not at all convinced that we want to universally enforce designated
initializers *yet*.  They make sense for _ops structures and other
structures that contain function pointers (which is a small subset), but
for many other structures they just add a large amount of noise to
initialization.  If we could say "automatically add __designated_init to
structures matching these criteria", that could work, but "all
structures" not so much.  (For instance, a structure with a single
field, or two fields of incompatible types with an intuitive ordering,
doesn't gain much value from designated initialization.)

Now, some cases may make sense to fix despite the large volume.  For
instance, many users of dmi_system_id initialize it positionally, and
shouldn't; I've already written patches for quite a few of them, with
many more to go.  But, for instance, obs_kernel_param seems much less
worthwhile to fix, because we shouldn't add any new instances of it, and
we likely won't ever extend it.  The value in fixing this issue mostly
comes not with the current code, but with future changes to the
structure.  (And, as always happens with this kind of change, we'll end
up with a few holdouts who don't want to change their code, which will
stop us from eliminating the warning completely.)

In the course of introducing this change, we'll end up fixing a large
number of positional init warnings.  If in the course of doing so, it
starts making sense to enforce it everywhere, it seems easy enough to
sed away all the __designated_init tags.  But in terms of noise, the
actual additions of __designated_init will pale in comparison to the
patches fixing positional initializers.

Finally, by migrating over to this incrementally, structure by
structure, we can safely make the warning an error, which we could not
do if we applied it to every struct immediately.

- Josh Triplett

^ permalink raw reply

* Re: [PATCH v2 08/25] amdkfd: Add IOCTL set definitions of amdkfd
From: Oded Gabbay @ 2014-08-02 20:00 UTC (permalink / raw)
  To: Jerome Glisse, linux-kernel, linux-api,
	dri-devel@lists.freedesktop.org
  Cc: Andrew Lewycky, Michel Dänzer, Alexey Skidanov,
	Andrew Morton
In-Reply-To: <20140720165432.GB3068@gmail.com>



On 20/07/14 19:54, Jerome Glisse wrote:
> On Thu, Jul 17, 2014 at 04:29:15PM +0300, Oded Gabbay wrote:
>> - 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
>>
>> - KFD_IOC_PMC_ACQUIRE_ACCESS:
>> 	Acquires exclusive access (from other HSA processes) to the performance
>> counters of the GPU
>>
>> - KFD_IOC_PMC_RELEASE_ACCESS:
>> 	Releases exclusive access of the GPU's performance counters
> 
> Exclusive userspace access is recipie for failure. This must go and you must
> come up with better model. Which in my mind involve an ioctl for each command
> buffer submission.
> 
Removed these 2 ioctls in v3
>>
>> Signed-off-by: Oded Gabbay <oded.gabbay@amd.com>
>> ---
>>  include/uapi/linux/kfd_ioctl.h | 133 +++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 133 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..3cedd1a
>> --- /dev/null
>> +++ b/include/uapi/linux/kfd_ioctl.h
>> @@ -0,0 +1,133 @@
>> +/*
>> + * 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
>> +
>> +/* The 64-bit ABI is the authoritative version. */
>> +#pragma pack(push, 1)
> 
> pagram pack must be remove do not use that.
> 
Removed in v3
>> +
>> +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_address;	/* 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 */
>> +};
> 
> The queue_percentage and queue_priority really needs some explanations. I guess
> userspace would shed some light on those but still this should be properly explain.
> 
Added explanation in v3
>> +
>> +/* 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 */
>> +};
> 
> Same what is aperture in this context. I know about all this stuff but i have no
> idea what aperture is in this context.
> 
Added explanation in v3
>> +
>> +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 */
>> +};
> 
> Again comment about what kind of counter this is, monotonic, ...
> 
Added explanation in v3
>> +
>> +#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]*/
>> +};
> 
> I would rather see userspace provide a pointer to an array and the size of that
> array and possibly size of individual element. This would allow you to grow the
> kfd_process_device_apertures if needs be. Thought as i understand it this is a
> temporary driver.
> 
This is not going to change in the near or far future. I also agree that
we would probably see a common framework before NUM_OF_SUPPORTED_GPUS
increases.
If you insist, I'll try to squeeze it in at v4
>> +
>> +struct kfd_ioctl_pmc_acquire_access_args {
>> +	uint64_t trace_id;		/* to KFD */
>> +	uint32_t gpu_id;		/* to KFD */
>> +};
>> +
>> +struct kfd_ioctl_pmc_release_access_args {
>> +	uint64_t trace_id;		/* to KFD */
>> +	uint32_t gpu_id;		/* to KFD */
>> +};
> 
> As said above no ioctl to have some exclusive access you can not trust userspace
> that's rules NUMBER ONE.
> 
Removed in v3.
>> +
>> +#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)
>> +#define KFD_IOC_PMC_ACQUIRE_ACCESS	_IOW(KFD_IOC_MAGIC, 12, struct kfd_ioctl_pmc_acquire_access_args)
>> +#define KFD_IOC_PMC_RELEASE_ACCESS	_IOW(KFD_IOC_MAGIC, 13, struct kfd_ioctl_pmc_release_access_args)
>> +
>> +#pragma pack(pop)
> 
> No pragma packing.
> 
Removed in v3
>> +
>> +#endif
>> -- 
>> 1.9.1
>>
Oded

^ permalink raw reply

* Re: [PATCH -v4] random: introduce getrandom(2) system call
From: Pavel Machek @ 2014-08-03 11:38 UTC (permalink / raw)
  To: Bernd Petrovitsch
  Cc: Bob Beck, Theodore Ts'o, linux-kernel, linux-api,
	linux-crypto, Theo de Raadt
In-Reply-To: <1406794012.26034.14.camel@thorin>

On Thu 2014-07-31 10:06:37, Bernd Petrovitsch wrote:
> On Don, 2014-07-31 at 00:18 +0200, Pavel Machek wrote:
> > On Wed 2014-07-30 16:40:52, Bernd Petrovitsch wrote:
> > > On Mit, 2014-07-30 at 07:56 -0600, Bob Beck wrote:
> > > > Pavel. I have bit 'ol enterprise daemon running with established file
> > > > descriptors serving thousands of connections
> > > > which periodically require entropy.  Now I run out of descriptors. I
> > > > can't establish new connections. but I should
> > > > now halt all the other ones that require entropy?  I should raise
> > > > SIGKILL on my process serving these thousands
> > > > of connetions?  I don't think so.
> > > 
> > > If that long-running daemon periodically needs something from a device,
> > > one would better keep the fd for that open the whole time. Saves some
> > > CPU cycles and latency too BTW.
> > 
> > Agreed.
> > 
> > On the other hand, keeping a fd open is quite tricky for a
> > library. But better solution might be to make that easier.
> 
> Yes, in a (full-fledged, standalone) library seems at least tricky (also
> referring to some off-list mails here: think about fork() - which could
> be inside system() or popen() or similar). 
> 
> But as part of the *application* (where one has control over fork()
> etc.), this should be somewhat less risky. Yes, that doesn't really help
> libssl;-)

...

> Hehe, we (Unix!) have (had) gettimeofday(), time() and similar sys-calls
> since ages and no one proposed to make devices for them and get rid of
> the system-calls.

Well, but we do open(/dev/sda), read(1); not read_from_sda(...). And
cat /dev/urandom > file is useful operation. So it is not like
getentropy() can replace /dev/*random.

> > open( , O_IM_A_LIBRARY_GIVE_ME_ONE_OF_THREE_RESERVED_FDS) might be one
> > solution. Actually, one reserved fd should be enough.
> 
> Well, this can also be DoSed and the proposal aims to make that
> impossible (and where does this reserved count against? process-limits,
> kernel-wide limit?).

Process limit. DoS should not be an issue here. Remember, we are doing
this to help libraries.
								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply

* [PATCH 0/7] locking/rwsem: enable reader opt-spinning & writer respin
From: Waiman Long @ 2014-08-04  2:36 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra
  Cc: linux-kernel, linux-api, linux-doc, Davidlohr Bueso, Jason Low,
	Scott J Norton, Waiman Long

This patch set improves upon the rwsem optimistic spinning patch set
from Davidlohr to enable better performing rwsem and more aggressive
use of optimistic spinning.

By using a microbenchmark running 1 million lock-unlock operations per
thread on a 4-socket 40-core Westmere-EX x86-64 test machine running
3.16-rc7 based kernels, the following table shows the execution times
with 2/10 threads running on different CPUs on the same socket where
load is the number of pause instructions in the critical section:

  lock/r:w ratio # of threads	Load:Execution Time (ms)
  -------------- ------------	------------------------
  mutex		      2		1:530.7, 5:406.0, 10:472.7
  mutex		     10		1:1848 , 5:2046 , 10:4394

Before patch:
  rwsem/0:1	      2		1:339.4, 5:368.9, 10:394.0
  rwsem/1:1	      2		1:2915 , 5:2621 , 10:2764
  rwsem/10:1	      2		1:891.2, 5:779.2, 10:827.2
  rwsem/0:1	     10		1:5618 , 5:5722 , 10:5683
  rwsem/1:1	     10		1:14562, 5:14561, 10:14770
  rwsem/10:1	     10		1:5914 , 5:5971 , 10:5912

After patch:
  rwsem/0:1	     2		1:161.1, 5:244.4, 10:271.4
  rwsem/1:1	     2		1:188.8, 5:212.4, 10:312.9
  rwsem/10:1	     2		1:168.8, 5:179.5, 10:209.8
  rwsem/0:1	    10		1:1306 , 5:1733 , 10:1998
  rwsem/1:1	    10		1:1512 , 5:1602 , 10:2093
  rwsem/10:1	    10		1:1267 , 5:1458 , 10:2233

% Change:
  rwsem/0:1	     2		1:-52.5%, 5:-33.7%, 10:-31.1%
  rwsem/1:1	     2		1:-93.5%, 5:-91.9%, 10:-88.7%
  rwsem/10:1	     2		1:-81.1%, 5:-77.0%, 10:-74.6%
  rwsem/0:1	    10		1:-76.8%, 5:-69.7%, 10:-64.8%
  rwsem/1:1	    10		1:-89.6%, 5:-89.0%, 10:-85.8%
  rwsem/10:1	    10		1:-78.6%, 5:-75.6%, 10:-62.2%

It can be seen that there is dramatic reduction in the execution
times. The new rwsem is now even faster than mutex whether it is all
writers or a mixture of writers and readers.

Running the AIM7 benchmarks on the same 40-core system (HT off),
the performance improvements on some of the workloads were as follows:

      Workload	     Before Patch	After Patch	% Change
      --------	     ------------	-----------	--------
  custom (200-1000)	446135		  477404	 +7.0%
  custom (1100-2000)	449665		  484734	 +7.8%
  high_systime		152437		  154217	 +1.2%
   (200-1000)
  high_systime		269695		  278942	 +3.4%
   (1100-2000)

Waiman Long (7):
  locking/rwsem: don't resched at the end of optimistic spinning
  locking/rwsem: more aggressive use of optimistic spinning
  locking/rwsem: check for active writer/spinner before wakeup
  locking/rwsem: threshold limited spinning for active readers
  locking/rwsem: move down rwsem_down_read_failed function
  locking/rwsem: enables optimistic spinning for readers
  locking/rwsem: allow waiting writers to go back to optimistic spinning

 include/linux/osq_lock.h    |    5 +
 include/linux/rwsem.h       |    7 +
 kernel/locking/rwsem-xadd.c |  328 ++++++++++++++++++++++++++++++++++---------
 kernel/locking/rwsem.c      |   17 ++-
 4 files changed, 288 insertions(+), 69 deletions(-)

^ permalink raw reply

* [PATCH 1/7] locking/rwsem: don't resched at the end of optimistic spinning
From: Waiman Long @ 2014-08-04  2:36 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra
  Cc: linux-kernel, linux-api, linux-doc, Davidlohr Bueso, Jason Low,
	Scott J Norton, Waiman Long
In-Reply-To: <1407119782-41119-1-git-send-email-Waiman.Long@hp.com>

For a fully preemptive kernel, a call to preempt_enable() could
potentially trigger a task rescheduling event. In the case of rwsem
optimistic spinning, the task has either gotten the lock or is going
to sleep soon. So there is no point to do rescheduling here.

Signed-off-by: Waiman Long <Waiman.Long@hp.com>
---
 kernel/locking/rwsem-xadd.c |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index a2391ac..d058946 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -385,7 +385,11 @@ static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
 	}
 	osq_unlock(&sem->osq);
 done:
-	preempt_enable();
+	/*
+	 * Don't need to do rescheduling here as we either got the lock or
+	 * is going to sleep soon.
+	 */
+	preempt_enable_no_resched();
 	return taken;
 }
 
-- 
1.7.1


^ permalink raw reply related

* [PATCH 2/7] locking/rwsem: more aggressive use of optimistic spinning
From: Waiman Long @ 2014-08-04  2:36 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, Davidlohr Bueso, Jason Low,
	Scott J Norton, Waiman Long
In-Reply-To: <1407119782-41119-1-git-send-email-Waiman.Long-VXdhtT5mjnY@public.gmane.org>

The rwsem_can_spin_on_owner() function currently allows optimistic
spinning only if the owner field is defined and is running. That is
too conservative as it will cause some tasks to miss the opportunity
of doing spinning in case the owner hasn't been able to set the owner
field in time or the lock has just become available.

This patch enables more aggressive use of optimistic spinning by
assuming that the lock is spinnable unless proved otherwise.

Signed-off-by: Waiman Long <Waiman.Long-VXdhtT5mjnY@public.gmane.org>
---
 kernel/locking/rwsem-xadd.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index d058946..dce22b8 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -285,7 +285,7 @@ static inline bool rwsem_try_write_lock_unqueued(struct rw_semaphore *sem)
 static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
 {
 	struct task_struct *owner;
-	bool on_cpu = false;
+	bool on_cpu = true;	/* Assume spinnable unless proved not to be */
 
 	if (need_resched())
 		return false;
-- 
1.7.1

^ permalink raw reply related

* [PATCH 3/7] locking/rwsem: check for active writer/spinner before wakeup
From: Waiman Long @ 2014-08-04  2:36 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra
  Cc: linux-kernel, linux-api, linux-doc, Davidlohr Bueso, Jason Low,
	Scott J Norton, Waiman Long
In-Reply-To: <1407119782-41119-1-git-send-email-Waiman.Long@hp.com>

On a highly contended rwsem, spinlock contention due to the slow
rwsem_wake() call can be a significant portion of the total CPU cycles
used. With writer lock stealing and writer optimistic spinning, there
is also a pretty good chance that the lock may have been stolen
before the waker wakes up the waiters. The woken tasks, if any,
will have to go back to sleep again.

This patch adds checking code at the beginning of the rwsem_wake()
and __rwsem_do_wake() function to look for spinner and active
writer respectively.  The presence of an active writer will abort the
wakeup operation.  The presence of a spinner will still allow wakeup
operation to proceed as long as the trylock operation succeeds. This
strikes a good balance between excessive spinlock contention especially
when there are a lot of active readers and a lot of failed fastpath
operations because there are tasks waiting in the queue.

Signed-off-by: Waiman Long <Waiman.Long@hp.com>
---
 include/linux/osq_lock.h    |    5 ++++
 kernel/locking/rwsem-xadd.c |   57 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 61 insertions(+), 1 deletions(-)

diff --git a/include/linux/osq_lock.h b/include/linux/osq_lock.h
index 90230d5..79db546 100644
--- a/include/linux/osq_lock.h
+++ b/include/linux/osq_lock.h
@@ -24,4 +24,9 @@ static inline void osq_lock_init(struct optimistic_spin_queue *lock)
 	atomic_set(&lock->tail, OSQ_UNLOCKED_VAL);
 }
 
+static inline bool osq_has_spinner(struct optimistic_spin_queue *lock)
+{
+	return atomic_read(&lock->tail) != OSQ_UNLOCKED_VAL;
+}
+
 #endif
diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index dce22b8..9f71a67 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -107,6 +107,37 @@ enum rwsem_wake_type {
 	RWSEM_WAKE_READ_OWNED	/* Waker thread holds the read lock */
 };
 
+#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
+/*
+ * return true if there is an active writer by checking the owner field which
+ * should be set (not RWSEM_READ_OWNED) if there is one.
+ */
+static inline bool rwsem_has_active_writer(struct rw_semaphore *sem)
+{
+	struct task_struct *owner = ACCESS_ONCE(sem->owner);
+
+	return owner && (owner != RWSEM_READ_OWNED);
+}
+
+/*
+ * Return true if the rwsem has active spinner
+ */
+static inline bool rwsem_has_spinner(struct rw_semaphore *sem)
+{
+	return osq_has_spinner(&sem->osq);
+}
+#else /* CONFIG_RWSEM_SPIN_ON_OWNER */
+static inline bool rwsem_has_active_writer(struct rw_semaphore *sem)
+{
+	return false;	/* Assume it has no active writer */
+}
+
+static inline bool rwsem_has_spinner(struct rw_semaphore *sem)
+{
+	return false;
+}
+#endif /* CONFIG_RWSEM_SPIN_ON_OWNER */
+
 /*
  * handle the lock release when processes blocked on it that can now run
  * - if we come here from up_xxxx(), then:
@@ -125,6 +156,15 @@ __rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type)
 	struct list_head *next;
 	long oldcount, woken, loop, adjustment;
 
+	/*
+	 * Abort the wakeup operation if there is an active writer as the
+	 * lock was stolen. up_write() should have cleared the owner field
+	 * before calling this function. If that field is now set, there must
+	 * be an active writer present.
+	 */
+	if (rwsem_has_active_writer(sem))
+		goto out;
+
 	waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);
 	if (waiter->type == RWSEM_WAITING_FOR_WRITE) {
 		if (wake_type == RWSEM_WAKE_ANY)
@@ -479,7 +519,22 @@ struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem)
 {
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&sem->wait_lock, flags);
+	/*
+	 * If a spinner is present, it is not necessary to do the wakeup.
+	 * Try to do wakeup when the trylock succeeds to avoid potential
+	 * spinlock contention which may introduce too much delay in the
+	 * unlock operation.
+	 *
+	 * In case the spinning writer is just going to break out of the loop,
+	 * it will still do a trylock in rwsem_down_write_failed() before
+	 * sleeping.
+	 */
+	if (rwsem_has_spinner(sem)) {
+		if (!raw_spin_trylock_irqsave(&sem->wait_lock, flags))
+			return sem;
+	} else {
+		raw_spin_lock_irqsave(&sem->wait_lock, flags);
+	}
 
 	/* do nothing if list empty */
 	if (!list_empty(&sem->wait_list))
-- 
1.7.1

^ permalink raw reply related

* [PATCH 4/7] locking/rwsem: threshold limited spinning for active readers
From: Waiman Long @ 2014-08-04  2:36 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra
  Cc: linux-kernel, linux-api, linux-doc, Davidlohr Bueso, Jason Low,
	Scott J Norton, Waiman Long
In-Reply-To: <1407119782-41119-1-git-send-email-Waiman.Long@hp.com>

Even thought only the writers can perform optimistic spinning, there
is still a chance that readers may take the lock before a spinning
writer can get it. In that case, the owner field will be NULL and the
spinning writer can spin indefinitely until its time quantum expires
when some lock owning readers are not running.

This patch tries to handle this special case by:
 1) setting the owner field to a special value RWSEM_READ_OWNED
    to indicate that the current or last owner is a reader.
 2) seting a threshold on how many times (currently 100) spinning will
    be done with active readers before giving up as there is no easy
    way to determine if all of them are currently running.

By doing so, it tries to strike a balance between giving up too early
and losing potential performance gain and wasting too many precious
CPU cycles when some lock owning readers are not running.

Signed-off-by: Waiman Long <Waiman.Long@hp.com>
---
 include/linux/rwsem.h       |    7 +++++++
 kernel/locking/rwsem-xadd.c |   23 ++++++++++++++++++++---
 kernel/locking/rwsem.c      |   17 ++++++++++++++++-
 3 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h
index 035d3c5..48e1e31 100644
--- a/include/linux/rwsem.h
+++ b/include/linux/rwsem.h
@@ -66,6 +66,13 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem)
 #endif
 
 #ifdef CONFIG_RWSEM_SPIN_ON_OWNER
+/*
+ * The owner field is set to RWSEM_READ_OWNED if the last owner(s) are
+ * readers. It is not reset until a writer takes over and set it to its
+ * task structure pointer or NULL when it frees the lock. So a value
+ * of RWSEM_READ_OWNED doesn't mean it currently has active readers.
+ */
+#define RWSEM_READ_OWNED	((struct task_struct *)-1)
 #define __RWSEM_OPT_INIT(lockname) , .osq = OSQ_LOCK_UNLOCKED, .owner = NULL
 #else
 #define __RWSEM_OPT_INIT(lockname)
diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index 9f71a67..576d4cd 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -304,6 +304,11 @@ static inline bool rwsem_try_write_lock(long count, struct rw_semaphore *sem)
 
 #ifdef CONFIG_RWSEM_SPIN_ON_OWNER
 /*
+ * Threshold for optimistic spinning on readers
+ */
+#define RWSEM_READ_SPIN_THRESHOLD	100
+
+/*
  * Try to acquire write lock before the writer has been put on wait queue.
  */
 static inline bool rwsem_try_write_lock_unqueued(struct rw_semaphore *sem)
@@ -332,7 +337,7 @@ static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
 
 	rcu_read_lock();
 	owner = ACCESS_ONCE(sem->owner);
-	if (owner)
+	if (owner && (owner != RWSEM_READ_OWNED))
 		on_cpu = owner->on_cpu;
 	rcu_read_unlock();
 
@@ -381,10 +386,17 @@ bool rwsem_spin_on_owner(struct rw_semaphore *sem, struct task_struct *owner)
 	return sem->owner == NULL;
 }
 
+/*
+ * With active writer, spinning is done by checking if that writer is on
+ * CPU. With active readers, there is no easy way to determine if all of
+ * them are active. So it fall back to spin a certain number of them
+ * RWSEM_READ_SPIN_THRESHOLD before giving up.
+ */
 static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
 {
 	struct task_struct *owner;
 	bool taken = false;
+	int  read_spincnt = 0;
 
 	preempt_disable();
 
@@ -397,8 +409,12 @@ static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
 
 	while (true) {
 		owner = ACCESS_ONCE(sem->owner);
-		if (owner && !rwsem_spin_on_owner(sem, owner))
+		if (owner == RWSEM_READ_OWNED) {
+			if (++read_spincnt > RWSEM_READ_SPIN_THRESHOLD)
+				break;
+		} else if (owner && !rwsem_spin_on_owner(sem, owner)) {
 			break;
+		}
 
 		/* wait_lock will be acquired if write_lock is obtained */
 		if (rwsem_try_write_lock_unqueued(sem)) {
@@ -412,7 +428,8 @@ static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
 		 * we're an RT task that will live-lock because we won't let
 		 * the owner complete.
 		 */
-		if (!owner && (need_resched() || rt_task(current)))
+		if ((!owner || (owner == RWSEM_READ_OWNED)) &&
+		    (need_resched() || rt_task(current)))
 			break;
 
 		/*
diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c
index e2d3bc7..9770439 100644
--- a/kernel/locking/rwsem.c
+++ b/kernel/locking/rwsem.c
@@ -18,6 +18,11 @@ static inline void rwsem_set_owner(struct rw_semaphore *sem)
 	sem->owner = current;
 }
 
+static inline void rwsem_set_owner_read(struct rw_semaphore *sem)
+{
+	sem->owner = RWSEM_READ_OWNED;
+}
+
 static inline void rwsem_clear_owner(struct rw_semaphore *sem)
 {
 	sem->owner = NULL;
@@ -28,6 +33,10 @@ static inline void rwsem_set_owner(struct rw_semaphore *sem)
 {
 }
 
+static inline void rwsem_set_owner_read(struct rw_semaphore *sem)
+{
+}
+
 static inline void rwsem_clear_owner(struct rw_semaphore *sem)
 {
 }
@@ -42,6 +51,7 @@ void __sched down_read(struct rw_semaphore *sem)
 	rwsem_acquire_read(&sem->dep_map, 0, 0, _RET_IP_);
 
 	LOCK_CONTENDED(sem, __down_read_trylock, __down_read);
+	rwsem_set_owner_read(sem);
 }
 
 EXPORT_SYMBOL(down_read);
@@ -53,8 +63,10 @@ int down_read_trylock(struct rw_semaphore *sem)
 {
 	int ret = __down_read_trylock(sem);
 
-	if (ret == 1)
+	if (ret == 1) {
 		rwsem_acquire_read(&sem->dep_map, 0, 1, _RET_IP_);
+		rwsem_set_owner_read(sem);
+	}
 	return ret;
 }
 
@@ -127,6 +139,7 @@ void downgrade_write(struct rw_semaphore *sem)
 	 */
 	rwsem_clear_owner(sem);
 	__downgrade_write(sem);
+	rwsem_set_owner_read(sem);
 }
 
 EXPORT_SYMBOL(downgrade_write);
@@ -139,6 +152,7 @@ void down_read_nested(struct rw_semaphore *sem, int subclass)
 	rwsem_acquire_read(&sem->dep_map, subclass, 0, _RET_IP_);
 
 	LOCK_CONTENDED(sem, __down_read_trylock, __down_read);
+	rwsem_set_owner_read(sem);
 }
 
 EXPORT_SYMBOL(down_read_nested);
@@ -159,6 +173,7 @@ void down_read_non_owner(struct rw_semaphore *sem)
 	might_sleep();
 
 	__down_read(sem);
+	rwsem_set_owner_read(sem);
 }
 
 EXPORT_SYMBOL(down_read_non_owner);
-- 
1.7.1


^ permalink raw reply related

* [PATCH 5/7] locking/rwsem: move down rwsem_down_read_failed function
From: Waiman Long @ 2014-08-04  2:36 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra
  Cc: linux-kernel, linux-api, linux-doc, Davidlohr Bueso, Jason Low,
	Scott J Norton, Waiman Long
In-Reply-To: <1407119782-41119-1-git-send-email-Waiman.Long@hp.com>

Move the rwsem_down_read_failed() function down to below the optimistic
spinning section before enabling optimistic spinning for the readers.
There is no change in code.

Signed-off-by: Waiman Long <Waiman.Long@hp.com>
---
 kernel/locking/rwsem-xadd.c |   96 +++++++++++++++++++++---------------------
 1 files changed, 48 insertions(+), 48 deletions(-)

diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index 576d4cd..86618ea 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -239,54 +239,6 @@ __rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type)
 	return sem;
 }
 
-/*
- * Wait for the read lock to be granted
- */
-__visible
-struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
-{
-	long count, adjustment = -RWSEM_ACTIVE_READ_BIAS;
-	struct rwsem_waiter waiter;
-	struct task_struct *tsk = current;
-
-	/* set up my own style of waitqueue */
-	waiter.task = tsk;
-	waiter.type = RWSEM_WAITING_FOR_READ;
-	get_task_struct(tsk);
-
-	raw_spin_lock_irq(&sem->wait_lock);
-	if (list_empty(&sem->wait_list))
-		adjustment += RWSEM_WAITING_BIAS;
-	list_add_tail(&waiter.list, &sem->wait_list);
-
-	/* we're now waiting on the lock, but no longer actively locking */
-	count = rwsem_atomic_update(adjustment, sem);
-
-	/* If there are no active locks, wake the front queued process(es).
-	 *
-	 * If there are no writers and we are first in the queue,
-	 * wake our own waiter to join the existing active readers !
-	 */
-	if (count == RWSEM_WAITING_BIAS ||
-	    (count > RWSEM_WAITING_BIAS &&
-	     adjustment != -RWSEM_ACTIVE_READ_BIAS))
-		sem = __rwsem_do_wake(sem, RWSEM_WAKE_ANY);
-
-	raw_spin_unlock_irq(&sem->wait_lock);
-
-	/* wait to be given the lock */
-	while (true) {
-		set_task_state(tsk, TASK_UNINTERRUPTIBLE);
-		if (!waiter.task)
-			break;
-		schedule();
-	}
-
-	tsk->state = TASK_RUNNING;
-
-	return sem;
-}
-
 static inline bool rwsem_try_write_lock(long count, struct rw_semaphore *sem)
 {
 	if (!(count & RWSEM_ACTIVE_MASK)) {
@@ -458,6 +410,54 @@ static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
 #endif
 
 /*
+ * Wait for the read lock to be granted
+ */
+__visible
+struct rw_semaphore __sched * rwsem_down_read_failed(struct rw_semaphore *sem)
+{
+	long count, adjustment = -RWSEM_ACTIVE_READ_BIAS;
+	struct rwsem_waiter waiter;
+	struct task_struct *tsk = current;
+
+	/* set up my own style of waitqueue */
+	waiter.task = tsk;
+	waiter.type = RWSEM_WAITING_FOR_READ;
+	get_task_struct(tsk);
+
+	raw_spin_lock_irq(&sem->wait_lock);
+	if (list_empty(&sem->wait_list))
+		adjustment += RWSEM_WAITING_BIAS;
+	list_add_tail(&waiter.list, &sem->wait_list);
+
+	/* we're now waiting on the lock, but no longer actively locking */
+	count = rwsem_atomic_update(adjustment, sem);
+
+	/* If there are no active locks, wake the front queued process(es).
+	 *
+	 * If there are no writers and we are first in the queue,
+	 * wake our own waiter to join the existing active readers !
+	 */
+	if (count == RWSEM_WAITING_BIAS ||
+	    (count > RWSEM_WAITING_BIAS &&
+	     adjustment != -RWSEM_ACTIVE_READ_BIAS))
+		sem = __rwsem_do_wake(sem, RWSEM_WAKE_ANY);
+
+	raw_spin_unlock_irq(&sem->wait_lock);
+
+	/* wait to be given the lock */
+	while (true) {
+		set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+		if (!waiter.task)
+			break;
+		schedule();
+	}
+
+	tsk->state = TASK_RUNNING;
+
+	return sem;
+}
+
+/*
  * Wait until we successfully acquire the write lock
  */
 __visible
-- 
1.7.1

^ permalink raw reply related

* [PATCH 6/7] locking/rwsem: enables optimistic spinning for readers
From: Waiman Long @ 2014-08-04  2:36 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra
  Cc: linux-kernel, linux-api, linux-doc, Davidlohr Bueso, Jason Low,
	Scott J Norton, Waiman Long
In-Reply-To: <1407119782-41119-1-git-send-email-Waiman.Long@hp.com>

This patch enables readers to go into the optimistic spinning loop
so that both the readers and writers can spin together. This could
speed up workloads that use both readers and writers.

Signed-off-by: Waiman Long <Waiman.Long@hp.com>
---
 kernel/locking/rwsem-xadd.c |  124 +++++++++++++++++++++++++++++++++++++------
 1 files changed, 108 insertions(+), 16 deletions(-)

diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index 86618ea..aafc9f0 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -279,6 +279,81 @@ static inline bool rwsem_try_write_lock_unqueued(struct rw_semaphore *sem)
 	}
 }
 
+/*
+ * Try to acquire read lock
+ *
+ * There is ambiguity when RWSEM_WAITING_BIAS < count < 0 as a writer may
+ * be active instead of having waiters. So we need to recheck the count
+ * under wait_lock to be sure.
+ */
+static inline bool rwsem_try_read_lock_unqueued(struct rw_semaphore *sem)
+{
+	long old, count = ACCESS_ONCE(sem->count);
+	bool taken = false;	/* True if lock taken */
+
+	while (!taken) {
+		if (count < RWSEM_WAITING_BIAS)
+			break;	/* Have writer and waiter */
+
+		old = count;
+		if (count >= 0 || count == RWSEM_WAITING_BIAS) {
+			count = cmpxchg(&sem->count, old,
+					old + RWSEM_ACTIVE_READ_BIAS);
+			if (count == old) {
+				/* Got the read lock */
+				ACCESS_ONCE(sem->owner) = RWSEM_READ_OWNED;
+				taken = true;
+				/*
+				 * Try to wake up readers if lock is free
+				 */
+				if ((count == RWSEM_WAITING_BIAS) &&
+				    raw_spin_trylock_irq(&sem->wait_lock)) {
+					if (!list_empty(&sem->wait_list))
+						goto wake_readers;
+					raw_spin_unlock_irq(&sem->wait_lock);
+				}
+			}
+		} else if (ACCESS_ONCE(sem->owner) == RWSEM_READ_OWNED) {
+			long threshold;
+
+			/*
+			 * RWSEM_WAITING_BIAS < count < 0
+			 */
+			raw_spin_lock_irq(&sem->wait_lock);
+			threshold = list_empty(&sem->wait_list)
+				  ? 0 : RWSEM_WAITING_BIAS;
+			count = ACCESS_ONCE(sem->count);
+			if (count < threshold) {
+				raw_spin_unlock_irq(&sem->wait_lock);
+				break;
+			}
+			old   = count;
+			count = cmpxchg(&sem->count, old,
+					old + RWSEM_ACTIVE_READ_BIAS);
+			if (count == old) {
+				taken = true;
+				ACCESS_ONCE(sem->owner) = RWSEM_READ_OWNED;
+				/*
+				 * Wake up pending readers, if any,
+				 * while holding the lock.
+				 */
+				if (threshold)
+					goto wake_readers;
+			}
+			raw_spin_unlock_irq(&sem->wait_lock);
+		} else {
+			break;
+		}
+	}
+	return taken;
+
+wake_readers:
+	__rwsem_do_wake(sem, RWSEM_WAKE_READ_OWNED);
+	raw_spin_unlock_irq(&sem->wait_lock);
+	return true;
+
+}
+
 static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
 {
 	struct task_struct *owner;
@@ -344,7 +419,8 @@ bool rwsem_spin_on_owner(struct rw_semaphore *sem, struct task_struct *owner)
  * them are active. So it fall back to spin a certain number of them
  * RWSEM_READ_SPIN_THRESHOLD before giving up.
  */
-static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
+static bool rwsem_optimistic_spin(struct rw_semaphore *sem,
+				  enum rwsem_waiter_type type)
 {
 	struct task_struct *owner;
 	bool taken = false;
@@ -368,11 +444,11 @@ static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
 			break;
 		}
 
-		/* wait_lock will be acquired if write_lock is obtained */
-		if (rwsem_try_write_lock_unqueued(sem)) {
-			taken = true;
+		taken = (type == RWSEM_WAITING_FOR_WRITE)
+		      ? rwsem_try_write_lock_unqueued(sem)
+		      : rwsem_try_read_lock_unqueued(sem);
+		if (taken)
 			break;
-		}
 
 		/*
 		 * When there's no owner, we might have preempted between the
@@ -403,7 +479,8 @@ done:
 }
 
 #else
-static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
+static bool rwsem_optimistic_spin(struct rw_semaphore *sem,
+				  enum rwsem_waiter_type type)
 {
 	return false;
 }
@@ -415,11 +492,21 @@ static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
 __visible
 struct rw_semaphore __sched * rwsem_down_read_failed(struct rw_semaphore *sem)
 {
-	long count, adjustment = -RWSEM_ACTIVE_READ_BIAS;
+	long count, adjustment = 0;
 	struct rwsem_waiter waiter;
 	struct task_struct *tsk = current;
 
-	/* set up my own style of waitqueue */
+	/* undo read bias from down_read operation, stop active locking */
+	count = rwsem_atomic_update(-RWSEM_ACTIVE_READ_BIAS, sem);
+
+	/* do optimistic spinning and steal lock if possible */
+	if (rwsem_optimistic_spin(sem, RWSEM_WAITING_FOR_READ))
+		return sem;
+
+	/*
+	 * Optimistic spinning failed, proceed to the slowpath
+	 * and block until we can acquire the sem.
+	 */
 	waiter.task = tsk;
 	waiter.type = RWSEM_WAITING_FOR_READ;
 	get_task_struct(tsk);
@@ -429,8 +516,11 @@ struct rw_semaphore __sched * rwsem_down_read_failed(struct rw_semaphore *sem)
 		adjustment += RWSEM_WAITING_BIAS;
 	list_add_tail(&waiter.list, &sem->wait_list);
 
-	/* we're now waiting on the lock, but no longer actively locking */
-	count = rwsem_atomic_update(adjustment, sem);
+	/* we're now waiting on the lock */
+	if (adjustment)
+		count = rwsem_atomic_update(adjustment, sem);
+	else
+		count = ACCESS_ONCE(sem->count);
 
 	/* If there are no active locks, wake the front queued process(es).
 	 *
@@ -438,8 +528,7 @@ struct rw_semaphore __sched * rwsem_down_read_failed(struct rw_semaphore *sem)
 	 * wake our own waiter to join the existing active readers !
 	 */
 	if (count == RWSEM_WAITING_BIAS ||
-	    (count > RWSEM_WAITING_BIAS &&
-	     adjustment != -RWSEM_ACTIVE_READ_BIAS))
+	   (count >  RWSEM_WAITING_BIAS && adjustment))
 		sem = __rwsem_do_wake(sem, RWSEM_WAKE_ANY);
 
 	raw_spin_unlock_irq(&sem->wait_lock);
@@ -471,7 +560,7 @@ struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem)
 	count = rwsem_atomic_update(-RWSEM_ACTIVE_WRITE_BIAS, sem);
 
 	/* do optimistic spinning and steal lock if possible */
-	if (rwsem_optimistic_spin(sem))
+	if (rwsem_optimistic_spin(sem, RWSEM_WAITING_FOR_WRITE))
 		return sem;
 
 	/*
@@ -542,9 +631,12 @@ struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem)
 	 * spinlock contention which may introduce too much delay in the
 	 * unlock operation.
 	 *
-	 * In case the spinning writer is just going to break out of the loop,
-	 * it will still do a trylock in rwsem_down_write_failed() before
-	 * sleeping.
+	 * In case the spinner is just going to break out of the loop, it
+	 * will still do a trylock in rwsem_down_write_failed() before
+	 * sleeping, or call __rwsem_do_wake() in rwsem_down_read_failed()
+	 * if it detects a free lock. In either cases, we won't have the
+	 * situation that the lock is free and no task is woken up from the
+	 * waiting queue.
 	 */
 	if (rwsem_has_spinner(sem)) {
 		if (!raw_spin_trylock_irqsave(&sem->wait_lock, flags))
-- 
1.7.1

^ permalink raw reply related

* [PATCH 7/7] locking/rwsem: allow waiting writers to go back to optimistic spinning
From: Waiman Long @ 2014-08-04  2:36 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra
  Cc: linux-kernel, linux-api, linux-doc, Davidlohr Bueso, Jason Low,
	Scott J Norton, Waiman Long
In-Reply-To: <1407119782-41119-1-git-send-email-Waiman.Long@hp.com>

More aggressive use of optimistic spinning and enabling readers to
participate in spinning may make tasks waiting in the queue harder
to get access to the semaphore, especially for writers who need
exclusive access.

This patch enables a waking writer to go back to the optimistic
spinning loop as long as the owner is running. This allows waiting
writers better access to the semaphore as well as reduce the size
of the waiting queue.

Signed-off-by: Waiman Long <Waiman.Long@hp.com>
---
 kernel/locking/rwsem-xadd.c |   38 +++++++++++++++++++++++++++++++-------
 1 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index aafc9f0..94b0124 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -484,6 +484,11 @@ static bool rwsem_optimistic_spin(struct rw_semaphore *sem,
 {
 	return false;
 }
+
+static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
+{
+	return false;
+}
 #endif
 
 /*
@@ -553,12 +558,14 @@ __visible
 struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem)
 {
 	long count;
-	bool waiting = true; /* any queued threads before us */
+	bool waiting; /* any queued threads before us */
+	bool respin;
 	struct rwsem_waiter waiter;
 
 	/* undo write bias from down_write operation, stop active locking */
 	count = rwsem_atomic_update(-RWSEM_ACTIVE_WRITE_BIAS, sem);
 
+optspin:
 	/* do optimistic spinning and steal lock if possible */
 	if (rwsem_optimistic_spin(sem, RWSEM_WAITING_FOR_WRITE))
 		return sem;
@@ -573,8 +580,7 @@ struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem)
 	raw_spin_lock_irq(&sem->wait_lock);
 
 	/* account for this before adding a new element to the list */
-	if (list_empty(&sem->wait_list))
-		waiting = false;
+	waiting = !list_empty(&sem->wait_list);
 
 	list_add_tail(&waiter.list, &sem->wait_list);
 
@@ -595,23 +601,41 @@ struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem)
 
 	/* wait until we successfully acquire the lock */
 	set_current_state(TASK_UNINTERRUPTIBLE);
-	while (true) {
+	respin = false;
+	while (!respin) {
 		if (rwsem_try_write_lock(count, sem))
 			break;
 		raw_spin_unlock_irq(&sem->wait_lock);
 
-		/* Block until there are no active lockers. */
-		do {
+		/*
+		 * Block until there are no active lockers or optimistic
+		 * spinning is possible.
+		 */
+		while (true) {
 			schedule();
 			set_current_state(TASK_UNINTERRUPTIBLE);
-		} while ((count = sem->count) & RWSEM_ACTIVE_MASK);
+			count = ACCESS_ONCE(sem->count);
+			if (!(count & RWSEM_ACTIVE_MASK))
+				break;
+			/*
+			 * Go back to optimistic spinning if possible
+			 */
+			if (rwsem_can_spin_on_owner(sem)) {
+				respin = true;
+				break;
+			}
+		}
 
 		raw_spin_lock_irq(&sem->wait_lock);
 	}
 	__set_current_state(TASK_RUNNING);
 
 	list_del(&waiter.list);
+	if (respin && list_empty(&sem->wait_list))
+		rwsem_atomic_update(-RWSEM_WAITING_BIAS, sem);
 	raw_spin_unlock_irq(&sem->wait_lock);
+	if (respin)
+		goto optspin;
 
 	return sem;
 }
-- 
1.7.1

^ permalink raw reply related

* Re: [PATCH 2/7] locking/rwsem: more aggressive use of optimistic spinning
From: Davidlohr Bueso @ 2014-08-04  4:09 UTC (permalink / raw)
  To: Waiman Long
  Cc: Ingo Molnar, Peter Zijlstra, linux-kernel, linux-api, linux-doc,
	Jason Low, Scott J Norton
In-Reply-To: <1407119782-41119-3-git-send-email-Waiman.Long@hp.com>

On Sun, 2014-08-03 at 22:36 -0400, Waiman Long wrote:
> The rwsem_can_spin_on_owner() function currently allows optimistic
> spinning only if the owner field is defined and is running. That is
> too conservative as it will cause some tasks to miss the opportunity
> of doing spinning in case the owner hasn't been able to set the owner
> field in time or the lock has just become available.
> 
> This patch enables more aggressive use of optimistic spinning by
> assuming that the lock is spinnable unless proved otherwise.
> 
> Signed-off-by: Waiman Long <Waiman.Long@hp.com>
> ---
>  kernel/locking/rwsem-xadd.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
> 
> diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
> index d058946..dce22b8 100644
> --- a/kernel/locking/rwsem-xadd.c
> +++ b/kernel/locking/rwsem-xadd.c
> @@ -285,7 +285,7 @@ static inline bool rwsem_try_write_lock_unqueued(struct rw_semaphore *sem)
>  static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
>  {
>  	struct task_struct *owner;
> -	bool on_cpu = false;
> +	bool on_cpu = true;	/* Assume spinnable unless proved not to be */

Nope, unfortunately we need as it fixes some pretty bad regressions when
dealing with multiple readers -- as readers do not deal with lock
ownership, so another thread can spin for too long in !owner. See commit
37e95624.

^ permalink raw reply

* Re: [PATCH 2/7] locking/rwsem: more aggressive use of optimistic spinning
From: Jason Low @ 2014-08-04  4:10 UTC (permalink / raw)
  To: Waiman Long
  Cc: Ingo Molnar, Peter Zijlstra, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, Davidlohr Bueso, Scott J Norton,
	Dave Chinner
In-Reply-To: <1407119782-41119-3-git-send-email-Waiman.Long-VXdhtT5mjnY@public.gmane.org>

On Sun, 2014-08-03 at 22:36 -0400, Waiman Long wrote:
> The rwsem_can_spin_on_owner() function currently allows optimistic
> spinning only if the owner field is defined and is running. That is
> too conservative as it will cause some tasks to miss the opportunity
> of doing spinning in case the owner hasn't been able to set the owner
> field in time or the lock has just become available.
> 
> This patch enables more aggressive use of optimistic spinning by
> assuming that the lock is spinnable unless proved otherwise.
> 
> Signed-off-by: Waiman Long <Waiman.Long-VXdhtT5mjnY@public.gmane.org>
> ---
>  kernel/locking/rwsem-xadd.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
> 
> diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
> index d058946..dce22b8 100644
> --- a/kernel/locking/rwsem-xadd.c
> +++ b/kernel/locking/rwsem-xadd.c
> @@ -285,7 +285,7 @@ static inline bool rwsem_try_write_lock_unqueued(struct rw_semaphore *sem)
>  static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
>  {
>  	struct task_struct *owner;
> -	bool on_cpu = false;
> +	bool on_cpu = true;	/* Assume spinnable unless proved not to be */

Hi,

So "on_cpu = true" was recently converted to "on_cpu = false" in order
to address issues such as a 5x performance regression in the xfs_repair
workload that was caused by the original rwsem optimistic spinning code.

However, patch 4 in this patchset does address some of the problems with
spinning when there are readers. CC'ing Dave Chinner, who did the
testing with the xfs_repair workload.

^ permalink raw reply

* Re: [PATCH 0/7] locking/rwsem: enable reader opt-spinning & writer respin
From: Davidlohr Bueso @ 2014-08-04  4:25 UTC (permalink / raw)
  To: Waiman Long
  Cc: Ingo Molnar, Peter Zijlstra, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, Jason Low, Scott J Norton
In-Reply-To: <1407119782-41119-1-git-send-email-Waiman.Long-VXdhtT5mjnY@public.gmane.org>

On Sun, 2014-08-03 at 22:36 -0400, Waiman Long wrote:
> This patch set improves upon the rwsem optimistic spinning patch set
> from Davidlohr to enable better performing rwsem and more aggressive
> use of optimistic spinning.
> 
> By using a microbenchmark running 1 million lock-unlock operations per
> thread on a 4-socket 40-core Westmere-EX x86-64 test machine running
> 3.16-rc7 based kernels, the following table shows the execution times
> with 2/10 threads running on different CPUs on the same socket where
> load is the number of pause instructions in the critical section:
> 
>   lock/r:w ratio # of threads	Load:Execution Time (ms)
>   -------------- ------------	------------------------
>   mutex		      2		1:530.7, 5:406.0, 10:472.7
>   mutex		     10		1:1848 , 5:2046 , 10:4394
> 
> Before patch:
>   rwsem/0:1	      2		1:339.4, 5:368.9, 10:394.0
>   rwsem/1:1	      2		1:2915 , 5:2621 , 10:2764
>   rwsem/10:1	      2		1:891.2, 5:779.2, 10:827.2
>   rwsem/0:1	     10		1:5618 , 5:5722 , 10:5683
>   rwsem/1:1	     10		1:14562, 5:14561, 10:14770
>   rwsem/10:1	     10		1:5914 , 5:5971 , 10:5912
> 
> After patch:
>   rwsem/0:1	     2		1:161.1, 5:244.4, 10:271.4
>   rwsem/1:1	     2		1:188.8, 5:212.4, 10:312.9
>   rwsem/10:1	     2		1:168.8, 5:179.5, 10:209.8
>   rwsem/0:1	    10		1:1306 , 5:1733 , 10:1998
>   rwsem/1:1	    10		1:1512 , 5:1602 , 10:2093
>   rwsem/10:1	    10		1:1267 , 5:1458 , 10:2233
> 
> % Change:
>   rwsem/0:1	     2		1:-52.5%, 5:-33.7%, 10:-31.1%
>   rwsem/1:1	     2		1:-93.5%, 5:-91.9%, 10:-88.7%
>   rwsem/10:1	     2		1:-81.1%, 5:-77.0%, 10:-74.6%
>   rwsem/0:1	    10		1:-76.8%, 5:-69.7%, 10:-64.8%
>   rwsem/1:1	    10		1:-89.6%, 5:-89.0%, 10:-85.8%
>   rwsem/10:1	    10		1:-78.6%, 5:-75.6%, 10:-62.2%

So at a very low level you see nicer results, which aren't really
translating to much of a significant impact at a higher level (aim7).

> It can be seen that there is dramatic reduction in the execution
> times. The new rwsem is now even faster than mutex whether it is all
> writers or a mixture of writers and readers.
> 
> Running the AIM7 benchmarks on the same 40-core system (HT off),
> the performance improvements on some of the workloads were as follows:
> 
>       Workload	     Before Patch	After Patch	% Change
>       --------	     ------------	-----------	--------
>   custom (200-1000)	446135		  477404	 +7.0%
>   custom (1100-2000)	449665		  484734	 +7.8%
>   high_systime		152437		  154217	 +1.2%
>    (200-1000)
>   high_systime		269695		  278942	 +3.4%
>    (1100-2000)

I worry about complicating rwsems even _more_ than they are, specially
for such a marginal gain. You might want to try other workloads -- ie:
postgresql (pgbench), I normally get pretty useful data when dealing
with rwsems.

^ permalink raw reply

* [PATCH 0/9] Add drm driver for Rockchip Socs
From: mark yao @ 2014-08-04  4:41 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao

From: mark yao <yzq@rock-chips.com>

This a series of patches is a DRM Driver for Rockchip Socs, driver provides
an abstraction for the graphics hardware, as lcd controller and
connector interface.

add rk3288 lcd controller.
add rk3288 lvds and rk3288 edp connector.

Tested on rk3288 sdk board, use lvds or edp connector, boot and display OK

mark yao (9):
  drm: Add drm driver for Rockchip Socs
  Add devicetree bindings for panels used by the Rockchip DRM
  drm: add driver for panels used by the Rockchip DRM
  Add devicetree bindings for Rockchip lcd controller
  drm: add Rockchip rk3288 lcd controller driver
  Add devicetree bindings for Rockchip Soc LVDS
  drm: add Rockchip Soc rk3288 lvds connector
  Add devicetree bindings for Rockchip Soc EDP
  drm: add Rockchip Soc rk3288 edp connector


-- 
1.7.9.5



^ permalink raw reply

* [PATCH 1/9] drm: Add drm driver for Rockchip Socs
From: mark yao @ 2014-08-04  4:45 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao
In-Reply-To: <1407127274-1356-1-git-send-email-mark.yao@rock-chips.com>

This patch is a DRM Driver for Rockchip Socs, driver provides an abstraction
for the graphics hardware, such as lcd controller and connector interface.

Signed-off-by: mark yao <yzq@rock-chips.com>
---
 drivers/gpu/drm/Kconfig                           |    2 +
 drivers/gpu/drm/Makefile                          |    1 +
 drivers/gpu/drm/rockchip/Kconfig                  |   40 ++
 drivers/gpu/drm/rockchip/Makefile                 |   12 +
 drivers/gpu/drm/rockchip/rockchip_drm_connector.c |  412 ++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_connector.h |   36 ++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c       |  600 +++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h       |  128 ++++
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c        |   48 ++
 drivers/gpu/drm/rockchip/rockchip_drm_fb.h        |   28 +
 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c     |   63 ++
 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h     |   24 +
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c       |  163 +++++
 drivers/gpu/drm/rockchip/rockchip_drm_gem.h       |   40 ++
 drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c      |  720 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h      |  131 ++++
 include/uapi/drm/rockchip_drm.h                   |  110 ++++
 17 files changed, 2558 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_connector.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_connector.h
 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_lcdc.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h
 create mode 100644 include/uapi/drm/rockchip_drm.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f512004..5951c2c 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -170,6 +170,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 dd2ba42..40babd2 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -51,6 +51,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..592e999
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -0,0 +1,40 @@
+config DRM_ROCKCHIP
+	tristate "DRM Support for Rockchip"
+	depends on DRM
+	select DRM_KMS_HELPER
+	select DRM_KMS_FB_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_GEM_CMA_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
+	select OF
+	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.
+
+config DRM_ROCKCHIP_LCDC
+	bool "ROCKCHIP DRM LCDC"
+	depends on DRM_ROCKCHIP
+	select FB_MODE_HELPERS
+	help
+	  Choose this option if you want to use Rockchip lcdc for DRM.
+	  The driver provides an abstraction for Rockchip lcd controller,
+	  lcd controller is the display interface from memory frame buffer
+	  to display device.
+
+config DRM_ROCKCHIP_CONNECTOR
+	bool "ROCKCHIP DRM CONNECTOR"
+	depends on OF && DRM_ROCKCHIP && DRM_ROCKCHIP_LCDC
+	select FB_MODE_HELPERS
+	select VIDEOMODE_HELPERS
+	help
+	  Choose this option if you want to use Rockchip Primary DISPLAY.
+	  The driver provides an abstraction for Rockchip display devices,
+	  such as lcd plane, lvds, edp , mipi, etc.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
new file mode 100644
index 0000000..45c9d50
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
+
+rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_gem.o \
+		rockchip_drm_fb.o rockchip_drm_fbdev.o
+
+obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o
+obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o
+obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_connector.c b/drivers/gpu/drm/rockchip/rockchip_drm_connector.c
new file mode 100644
index 0000000..337b618
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_connector.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_dpi.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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+
+#include <linux/component.h>
+
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_connector.h"
+#include "rockchip_drm_lcdc.h"
+
+struct rockchip_conn_context {
+	struct device *dev;
+
+	struct drm_panel *panel;
+	struct rockchip_connector *conn;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+
+	struct drm_display_mode mode;
+
+	u32 flags;
+	int type;
+
+	int dpms_mode;
+};
+
+#define connector_to_ctx(c) \
+		container_of(c, struct rockchip_conn_context, connector)
+
+#define encoder_to_ctx(c) \
+		container_of(c, struct rockchip_conn_context, encoder)
+
+static inline int rockchip_convert_conn_type(int type)
+{
+	switch (type) {
+	case ROCKCHIP_DISPLAY_TYPE_RGB:
+	case ROCKCHIP_DISPLAY_TYPE_LVDS:
+		return DRM_MODE_CONNECTOR_LVDS;
+	case ROCKCHIP_DISPLAY_TYPE_EDP:
+		return DRM_MODE_CONNECTOR_eDP;
+	}
+
+	return  DRM_MODE_CONNECTOR_Unknown;
+}
+
+static inline int rockchip_convert_encoder_type(int type)
+{
+	switch (type) {
+	case ROCKCHIP_DISPLAY_TYPE_RGB:
+	case ROCKCHIP_DISPLAY_TYPE_LVDS:
+	case ROCKCHIP_DISPLAY_TYPE_EDP:
+		return DRM_MODE_ENCODER_LVDS;
+	}
+
+	return DRM_MODE_ENCODER_NONE;
+}
+
+static enum drm_connector_status
+rockchip_conn_detect(struct drm_connector *connector, bool force)
+{
+	return true;
+}
+
+static void rockchip_connector_destroy(struct drm_connector *connector)
+{
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs rockchip_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = rockchip_conn_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = rockchip_connector_destroy,
+};
+
+static int rockchip_conn_get_modes(struct drm_connector *connector)
+{
+	struct rockchip_conn_context *ctx = connector_to_ctx(connector);
+	struct drm_panel *panel = ctx->panel;
+
+	return panel->funcs->get_modes(panel);
+}
+
+static struct drm_encoder *
+	rockchip_conn_best_encoder(struct drm_connector *connector)
+{
+	struct rockchip_conn_context *ctx = connector_to_ctx(connector);
+
+	return &ctx->encoder;
+}
+
+static struct drm_connector_helper_funcs rockchip_connector_helper_funcs = {
+	.get_modes = rockchip_conn_get_modes,
+	.best_encoder = rockchip_conn_best_encoder,
+};
+
+static void rockchip_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct rockchip_conn_context *ctx = encoder_to_ctx(encoder);
+	struct rockchip_connector *conn = ctx->conn;
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		if (ctx->dpms_mode != DRM_MODE_DPMS_ON) {
+			conn->enable(conn);
+			ctx->panel->funcs->enable(ctx->panel);
+		}
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		if (ctx->dpms_mode == DRM_MODE_DPMS_ON) {
+			ctx->panel->funcs->disable(ctx->panel);
+			conn->disable(conn);
+		}
+		break;
+	default:
+		break;
+	}
+
+	ctx->dpms_mode = mode;
+}
+
+static bool
+rockchip_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+				const struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	struct rockchip_panel_special *priv_mode =
+					(void *)adjusted_mode->private;
+	struct rockchip_conn_context *ctx = encoder_to_ctx(encoder);
+
+	priv_mode->out_type = ctx->conn->type;
+
+	return true;
+}
+
+static void rockchip_drm_encoder_mode_set(struct drm_encoder *encoder,
+					  struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	/* just set dummy now */
+}
+
+static void rockchip_drm_encoder_prepare(struct drm_encoder *encoder)
+{
+	/* drm framework doesn't check NULL. */
+}
+
+static void rockchip_drm_encoder_commit(struct drm_encoder *encoder)
+{
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void rockchip_drm_encoder_disable(struct drm_encoder *encoder)
+{
+	struct drm_plane *plane;
+	struct drm_device *dev = encoder->dev;
+
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	/* all planes connected to this encoder should be also disabled. */
+	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+		if (plane->crtc && (plane->crtc == encoder->crtc))
+			plane->funcs->disable_plane(plane);
+	}
+}
+
+
+static struct drm_encoder_helper_funcs rockchip_encoder_helper_funcs = {
+	.dpms = rockchip_drm_encoder_dpms,
+	.mode_fixup = rockchip_drm_encoder_mode_fixup,
+	.mode_set = rockchip_drm_encoder_mode_set,
+	.prepare = rockchip_drm_encoder_prepare,
+	.commit = rockchip_drm_encoder_commit,
+	.disable = rockchip_drm_encoder_disable,
+};
+static void rockchip_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs rockchip_encoder_funcs = {
+	.destroy = rockchip_drm_encoder_destroy,
+};
+
+static unsigned int rockchip_drm_encoder_clones(struct drm_encoder *encoder)
+{
+	struct rockchip_conn_context *ctx = encoder_to_ctx(encoder);
+	struct drm_encoder *clone;
+	struct drm_device *dev = encoder->dev;
+	unsigned int clone_mask = 0;
+	int cnt = 0;
+
+	list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
+		switch (ctx->type) {
+		case ROCKCHIP_DISPLAY_TYPE_RGB:
+		case ROCKCHIP_DISPLAY_TYPE_LVDS:
+		case ROCKCHIP_DISPLAY_TYPE_EDP:
+		case ROCKCHIP_DISPLAY_TYPE_HDMI:
+			clone_mask |= (1 << (cnt++));
+			break;
+		default:
+			continue;
+		}
+	}
+
+	return clone_mask;
+}
+
+static int rockchip_conn_bind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct rockchip_conn_context *ctx;
+	unsigned long possible_crtcs = 0;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct device_node *panel_node;
+	struct drm_device *drm_dev = data;
+	int ret;
+
+	ctx = rockchip_drm_component_data_get(dev,
+					      ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+	if (!ctx) {
+		DRM_ERROR("can't find dp content form component\n");
+		return -EINVAL;
+	}
+
+	ret = rockchip_drm_pipe_get(dev);
+	if (ret < 0) {
+		DRM_ERROR("failed to bind display port\n");
+		return ret;
+	}
+
+	possible_crtcs |= 1 << ret;
+
+	encoder = &ctx->encoder;
+	encoder->possible_crtcs = possible_crtcs;
+
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_encoder_funcs,
+			       rockchip_convert_encoder_type(ctx->type));
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &rockchip_encoder_helper_funcs);
+
+	connector = &ctx->connector;
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &rockchip_connector_funcs,
+				 rockchip_convert_conn_type(ctx->type));
+	if (ret) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector,
+				 &rockchip_connector_helper_funcs);
+
+	ret = drm_sysfs_connector_add(connector);
+	if (ret) {
+		DRM_ERROR("failed to add drm_sysfs\n");
+		goto err_free_connector;
+	}
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector_sysfs;
+	}
+
+	panel_node = of_parse_phandle(dev->of_node, "rockchip,panel", 0);
+	if (!panel_node) {
+		DRM_ERROR("failed to find diaplay panel\n");
+		goto err_free_connector_sysfs;
+	}
+	ctx->panel = of_drm_find_panel(panel_node);
+	if (!ctx->panel) {
+		DRM_ERROR("failed to find diaplay panel\n");
+		ret = -ENODEV;
+		goto err_free_connector_sysfs;
+	}
+
+	of_node_put(panel_node);
+
+	ret = drm_panel_attach(ctx->panel, connector);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector_sysfs;
+	}
+
+	return 0;
+
+err_free_connector_sysfs:
+	drm_sysfs_connector_remove(connector);
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void rockchip_conn_unbind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct rockchip_conn_context *ctx;
+	struct drm_encoder *encoder;
+
+	ctx = rockchip_drm_component_data_get(dev,
+					      ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+	encoder = &ctx->encoder;
+
+	drm_panel_detach(ctx->panel);
+
+	rockchip_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+	encoder->funcs->destroy(encoder);
+	drm_sysfs_connector_remove(&ctx->connector);
+	drm_connector_cleanup(&ctx->connector);
+	drm_encoder_cleanup(encoder);
+
+	rockchip_drm_component_del(dev, ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+}
+
+static const struct component_ops rockchip_conn_component_ops = {
+	.bind = rockchip_conn_bind,
+	.unbind = rockchip_conn_unbind,
+};
+
+void *rockchip_connector_register(struct rockchip_connector *conn)
+{
+	struct rockchip_conn_context *ctx;
+	struct device *dev = conn->dev;
+	int ret;
+
+	if (!dev) {
+		DRM_ERROR("please provide a device at dp register\n");
+		return NULL;
+	}
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return NULL;
+
+	ret = rockchip_drm_component_add(dev, ROCKCHIP_DEVICE_TYPE_CONNECTOR,
+					 conn->type, ctx);
+	if (ret < 0) {
+		DRM_ERROR("register connector component fail ret =%d\n", ret);
+		return NULL;
+	}
+
+	ctx->conn = conn;
+	ctx->dev = dev;
+	ctx->type = conn->type;
+	ctx->dpms_mode = DRM_MODE_DPMS_OFF;
+
+	ret = component_add(dev, &rockchip_conn_component_ops);
+	if (ret)
+		goto err_del_component;
+
+	DRM_DEBUG_KMS("succes register connector type=%d\n", conn->type);
+
+	return ctx;
+
+err_del_component:
+	rockchip_drm_component_del(dev, ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+	return NULL;
+}
+
+void rockchip_connector_unregister(void *data)
+{
+	struct rockchip_conn_context *ctx = data;
+
+	if (!ctx)
+		return;
+	rockchip_drm_component_del(ctx->dev, ROCKCHIP_DEVICE_TYPE_CONNECTOR);
+	component_del(ctx->dev, &rockchip_conn_component_ops);
+}
+
+void rockchip_drm_encoder_setup(struct drm_device *dev)
+{
+	struct drm_encoder *encoder;
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+		encoder->possible_clones =
+				rockchip_drm_encoder_clones(encoder);
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_connector.h b/drivers/gpu/drm/rockchip/rockchip_drm_connector.h
new file mode 100644
index 0000000..191f9fc
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_connector.h
@@ -0,0 +1,36 @@
+/*
+ * 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_CONNECTOR_H_
+#define _ROCKCHIP_DRM_CONNECTOR_H_
+
+#include <drm/drm_crtc.h>
+
+#include "rockchip_drm_drv.h"
+
+struct rockchip_connector {
+	struct device *dev;
+	int type;
+	void *priv;
+	u32 flags;
+
+	void (*enable)(struct rockchip_connector *conn);
+	void (*disable)(struct rockchip_connector *conn);
+	int (*setmode)(struct rockchip_connector *conn,
+		       struct drm_display_mode *mode);
+};
+
+void *rockchip_connector_register(struct rockchip_connector *conn);
+void rockchip_connector_unregister(void *data);
+#endif
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..4871867
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -0,0 +1,600 @@
+/*
+ * 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 <linux/pm_runtime.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include <linux/anon_inodes.h>
+#include <linux/component.h>
+
+#include <drm/rockchip_drm.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-drm"
+#define DRIVER_DESC	"RockChip Soc DRM"
+#define DRIVER_DATE	"20140725"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+#define VBLANK_OFF_DELAY	50000
+
+static struct platform_device *rockchip_drm_pdev;
+
+static DEFINE_MUTEX(drm_component_lock);
+static LIST_HEAD(drm_component_list);
+
+struct component_dev {
+	struct list_head list;
+	struct device *crtc_dev;
+	struct device *conn_dev;
+	unsigned int out_type;
+	int pipe;
+	void *crtc_data;
+	void *conn_data;
+};
+
+static int rockchip_drm_load(struct drm_device *dev, unsigned long flags)
+{
+	struct rockchip_drm_private *private;
+	int ret;
+	int nr;
+
+	private = kzalloc(sizeof(*private), GFP_KERNEL);
+	if (!private)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev->dev, dev);
+	dev->dev_private = (void *)private;
+
+	drm_mode_config_init(dev);
+
+	rockchip_drm_mode_config_init(dev);
+
+
+	/* Try to bind all sub drivers. */
+	ret = component_bind_all(dev->dev, dev);
+	if (ret)
+		goto err_cleanup_vblank;
+
+	for (nr = 0; nr < MAX_PLANE; nr++) {
+		struct drm_plane *plane;
+		unsigned long possible_crtcs = (1 << MAX_CRTC) - 1;
+
+		plane = rockchip_plane_init(dev, possible_crtcs, false);
+		if (!plane)
+			goto err_mode_config_cleanup;
+	}
+
+	/* init kms poll for handling hpd */
+	drm_kms_helper_poll_init(dev);
+
+	ret = drm_vblank_init(dev, MAX_CRTC);
+	if (ret)
+		goto err_mode_config_cleanup;
+
+	/* setup possible_clones. */
+	rockchip_drm_encoder_setup(dev);
+
+	drm_vblank_offdelay = VBLANK_OFF_DELAY;
+
+	platform_set_drvdata(dev->platformdev, dev);
+	rockchip_drm_fbdev_init(dev);
+
+	/* force connectors detection */
+	drm_helper_hpd_irq_event(dev);
+
+	return 0;
+
+err_cleanup_vblank:
+	drm_vblank_cleanup(dev);
+err_mode_config_cleanup:
+	drm_mode_config_cleanup(dev);
+	kfree(private);
+
+	return ret;
+}
+
+static int rockchip_drm_unload(struct drm_device *dev)
+{
+	rockchip_drm_fbdev_fini(dev);
+	drm_vblank_cleanup(dev);
+	drm_kms_helper_poll_fini(dev);
+	drm_mode_config_cleanup(dev);
+
+	kfree(dev->dev_private);
+
+	component_unbind_all(dev->dev, dev);
+	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;
+}
+
+static int rockchip_drm_open(struct drm_device *dev, struct drm_file *file)
+{
+	return 0;
+}
+
+static void rockchip_drm_postclose(struct drm_device *dev,
+				   struct drm_file *file)
+{
+	struct drm_pending_event *e, *et;
+	unsigned long flags;
+
+	if (!file->driver_priv)
+		return;
+
+	/* Release all events not unhandled by page flip handler. */
+	rockchip_drm_crtc_cancel_pending_flip(dev);
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	/* Release all events handled by page flip handler but not freed. */
+	list_for_each_entry_safe(e, et, &file->event_list, link) {
+		list_del(&e->link);
+		e->destroy(e);
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	kfree(file->driver_priv);
+	file->driver_priv = NULL;
+}
+
+static const struct drm_ioctl_desc rockchip_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_drm_gem_create_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
+			  rockchip_drm_gem_map_offset_ioctl, DRM_UNLOCKED |
+			  DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MMAP, rockchip_drm_gem_mmap_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET, rockchip_drm_gem_get_ioctl,
+			  DRM_UNLOCKED),
+};
+
+static const struct file_operations rockchip_drm_driver_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.mmap = drm_gem_cma_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+	.unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = drm_compat_ioctl,
+#endif
+	.release = drm_release,
+};
+
+static struct drm_driver rockchip_drm_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+	.load			= rockchip_drm_load,
+	.unload			= rockchip_drm_unload,
+	.suspend		= rockchip_drm_suspend,
+	.resume			= rockchip_drm_resume,
+	.open			= rockchip_drm_open,
+	.postclose		= rockchip_drm_postclose,
+	.get_vblank_counter	= drm_vblank_count,
+	.enable_vblank		= rockchip_drm_crtc_enable_vblank,
+	.disable_vblank		= rockchip_drm_crtc_disable_vblank,
+	.gem_free_object        = drm_gem_cma_free_object,
+	.gem_vm_ops             = &drm_gem_cma_vm_ops,
+	.dumb_create            = drm_gem_cma_dumb_create,
+	.dumb_map_offset        = drm_gem_cma_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 = drm_gem_cma_prime_get_sg_table,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_vmap         = drm_gem_cma_prime_vmap,
+	.gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
+	.gem_prime_mmap         = drm_gem_cma_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)
+};
+
+int rockchip_drm_pipe_get(struct device *dev)
+{
+	struct component_dev *cdev, *next;
+	int pipe = -1;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+		if ((cdev->crtc_dev == dev) || (cdev->conn_dev == dev)) {
+			pipe = cdev->pipe;
+			break;
+		}
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	return pipe;
+}
+
+int rockchip_drm_out_type_get(struct device *dev)
+{
+	struct component_dev *cdev, *next;
+	int type = -1;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+		if ((cdev->crtc_dev == dev) || (cdev->conn_dev == dev)) {
+			type = cdev->out_type;
+			break;
+		}
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	return type;
+}
+
+void *rockchip_drm_component_data_get(struct device *dev,
+				      enum rockchip_drm_device_type dev_type)
+{
+	struct component_dev *cdev, *next;
+	void *data = NULL;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+		if ((cdev->crtc_dev == dev) || (cdev->conn_dev == dev)) {
+			if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC)
+				data = cdev->crtc_data;
+			else if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR)
+				data = cdev->conn_data;
+			break;
+		}
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	return data;
+}
+
+int rockchip_drm_component_add(struct device *dev,
+			       enum rockchip_drm_device_type dev_type,
+			       int out_type, void *data)
+{
+	struct component_dev *cdev;
+	int pipe = -1;
+
+	if (dev_type != ROCKCHIP_DEVICE_TYPE_CRTC &&
+	    dev_type != ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
+		DRM_ERROR("invalid device type.\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&drm_component_lock);
+
+	/*
+	 * Make sure to check if there is a component which has two device
+	 * objects, for connector and for encoder/connector.
+	 * It should make sure that crtc and encoder/connector drivers are
+	 * ready before rockchip drm core binds them.
+	 */
+	list_for_each_entry(cdev, &drm_component_list, list) {
+		pipe++;
+		/*
+		 * out_type from crtc and display port, crtc set possible
+		 * out_type maskbit at out_type, and display posr set out_type
+		 * directly. and if crtc and display port all register, set
+		 * out_type not maskbit;
+		 */
+		if (cdev->out_type & out_type) {
+			if (cdev->crtc_dev && cdev->conn_dev) {
+				DRM_ERROR("already register, not allow");
+				return -EINVAL;
+			}
+
+			if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC) {
+				cdev->pipe = pipe;
+				cdev->crtc_dev = dev;
+				cdev->crtc_data = data;
+			} else if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
+				cdev->conn_dev = dev;
+				cdev->conn_data = data;
+				cdev->out_type = out_type;
+			}
+
+			mutex_unlock(&drm_component_lock);
+			return 0;
+		}
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+	if (!cdev)
+		return -ENOMEM;
+
+	if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC) {
+		cdev->crtc_dev = dev;
+		cdev->crtc_data = data;
+	} else if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
+		cdev->conn_dev = dev;
+		cdev->conn_data = data;
+	}
+
+	cdev->out_type = out_type;
+
+	mutex_lock(&drm_component_lock);
+	list_add_tail(&cdev->list, &drm_component_list);
+	mutex_unlock(&drm_component_lock);
+
+	return 0;
+}
+
+void rockchip_drm_component_del(struct device *dev,
+				enum rockchip_drm_device_type dev_type)
+{
+	struct component_dev *cdev, *next;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+		if (dev_type == ROCKCHIP_DEVICE_TYPE_CRTC) {
+			if (cdev->crtc_dev == dev)
+				cdev->crtc_dev = NULL;
+		}
+
+		if (dev_type == ROCKCHIP_DEVICE_TYPE_CONNECTOR) {
+			if (cdev->conn_dev == dev)
+				cdev->conn_dev = NULL;
+		}
+
+		/*
+		 * Release cdev object only in case that both of crtc and
+		 * encoder/connector device objects are NULL.
+		 */
+		if (!cdev->crtc_dev && !cdev->conn_dev) {
+			list_del(&cdev->list);
+			kfree(cdev);
+		}
+
+		break;
+	}
+
+	mutex_unlock(&drm_component_lock);
+}
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev == (struct device *)data;
+}
+
+static int rockchip_drm_add_components(struct device *dev, struct master *m)
+{
+	struct component_dev *cdev;
+	unsigned int attach_cnt = 0;
+
+	mutex_lock(&drm_component_lock);
+
+	list_for_each_entry(cdev, &drm_component_list, list) {
+		int ret;
+
+		/*
+		 * Add components to master only in case that crtc and
+		 * encoder/connector device objects exist.
+		 */
+		if (!cdev->crtc_dev || !cdev->conn_dev)
+			continue;
+
+		attach_cnt++;
+
+		mutex_unlock(&drm_component_lock);
+
+		/*
+		 * Do not chage below call order.
+		 * crtc device first should be added to master because
+		 * connector/encoder need pipe number of crtc when they
+		 * are created.
+		 */
+		ret = component_master_add_child(m, compare_of, cdev->crtc_dev);
+		ret |= component_master_add_child(m, compare_of,
+						  cdev->conn_dev);
+		if (ret < 0)
+			return ret;
+
+		mutex_lock(&drm_component_lock);
+	}
+
+	mutex_unlock(&drm_component_lock);
+
+	return attach_cnt ? 0 : -ENODEV;
+}
+
+static int rockchip_drm_bind(struct device *dev)
+{
+	return drm_platform_init(&rockchip_drm_driver, to_platform_device(dev));
+}
+
+static void rockchip_drm_unbind(struct device *dev)
+{
+	drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops rockchip_drm_ops = {
+	.add_components = rockchip_drm_add_components,
+	.bind = rockchip_drm_bind,
+	.unbind = rockchip_drm_unbind,
+};
+
+static int rockchip_drm_platform_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	rockchip_drm_driver.num_ioctls = ARRAY_SIZE(rockchip_ioctls);
+
+	ret = component_master_add(&pdev->dev, &rockchip_drm_ops);
+	if (ret < 0)
+		DRM_DEBUG_KMS("re-tried by last sub driver probed later.\n");
+
+	return 0;
+}
+
+static int rockchip_drm_platform_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &rockchip_drm_ops);
+
+	return 0;
+}
+
+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",
+		.pm = &rockchip_drm_pm_ops,
+	},
+};
+
+static int rockchip_drm_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&rockchip_panel_platform_driver);
+	if (ret < 0)
+		return -ENOMEM;
+
+#ifdef CONFIG_DRM_ROCKCHIP_LCDC
+	ret = platform_driver_register(&rockchip_lcdc_platform_driver);
+	if (ret < 0)
+		goto out_lcdc;
+#endif
+
+	rockchip_drm_pdev = platform_device_register_simple("rockchip-drm", -1,
+							    NULL, 0);
+	if (IS_ERR(rockchip_drm_pdev)) {
+		ret = PTR_ERR(rockchip_drm_pdev);
+		goto out_drm_pdev;
+	}
+
+	ret = platform_driver_register(&rockchip_drm_platform_driver);
+	if (ret)
+		goto out_drm_driver;
+
+	return 0;
+
+out_drm_driver:
+	platform_device_unregister(rockchip_drm_pdev);
+out_drm_pdev:
+#ifdef CONFIG_DRM_ROCKCHIP_LCDC
+	platform_driver_unregister(&rockchip_lcdc_platform_driver);
+out_lcdc:
+#endif
+	platform_driver_unregister(&rockchip_panel_platform_driver);
+	return ret;
+}
+
+static void rockchip_drm_exit(void)
+{
+	platform_device_unregister(rockchip_drm_pdev);
+	platform_driver_unregister(&rockchip_drm_platform_driver);
+#ifdef CONFIG_DRM_ROCKCHIP_LCDC
+	platform_driver_unregister(&rockchip_lcdc_platform_driver);
+#endif
+	platform_driver_unregister(&rockchip_panel_platform_driver);
+}
+
+module_init(rockchip_drm_init);
+module_exit(rockchip_drm_exit);
+
+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..c0c1d89
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -0,0 +1,128 @@
+/*
+ * 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>
+
+#define MAX_CRTC	3
+#define MAX_PLANE	5
+#define MAX_FB_BUFFER	4
+#define DEFAULT_ZPOS	-1
+
+struct drm_device;
+struct drm_connector;
+
+/*
+ * display output interface supported by rockchip lcdc
+ */
+#define ROCKCHIP_OUTFACE_P888	0
+#define ROCKCHIP_OUTFACE_P666	1
+#define ROCKCHIP_OUTFACE_P565	2
+/* for use special outface */
+#define ROCKCHIP_OUTFACE_AAAA	15
+
+#define ROCKCHIP_COLOR_SWAP_RG	0x1
+#define ROCKCHIP_COLOR_SWAP_RB	0x2
+#define ROCKCHIP_COLOR_SWAP_GB	0x4
+/*
+ * Special panel info for rockchip
+ *
+ * @out_type: lcd controller need to know the sceen type.
+ * @out_face: the output pin interface.
+ * @color_swap: if want to swap color at output, use this.
+ * @pwr18: choice the power supply 1.8 or 3.3 mode for lcdc
+ * @dither: use dither func at lcd output
+ * @flags: the display flags, now just for pin sync level.
+ */
+struct rockchip_panel_special {
+	int out_type;
+	int out_face;
+	u32 color_swap;
+	bool pwr18;
+	bool dither;
+	u32 flags;
+};
+
+/* 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,
+	/* RGB Interface. */
+	ROCKCHIP_DISPLAY_TYPE_RGB = (1 << 0),
+	/* LVDS Interface. */
+	ROCKCHIP_DISPLAY_TYPE_LVDS = (1 << 1),
+	/* DUAL LVDS Interface. */
+	ROCKCHIP_DISPLAY_TYPE_DUAL_LVDS = (1 << 2),
+	/* EDP Interface. */
+	ROCKCHIP_DISPLAY_TYPE_EDP = (1 << 3),
+	/* MIPI Interface. */
+	ROCKCHIP_DISPLAY_TYPE_MIPI = (1 << 4),
+	/* HDMI Interface. */
+	ROCKCHIP_DISPLAY_TYPE_HDMI = (1 << 5),
+};
+
+/*
+ * Rockchip drm private structure.
+ *
+ * @pipe: the pipe number for this crtc/manager.
+ */
+struct rockchip_drm_private {
+	struct drm_fbdev_cma *fbdev_cma;
+	/*
+	 * created crtc object would be contained at this array and
+	 * this array is used to be aware of which crtc did it request vblank.
+	 */
+	struct drm_crtc *crtc[MAX_CRTC];
+	struct drm_property *plane_zpos_property;
+	struct drm_property *crtc_mode_property;
+
+	unsigned int pipe;
+};
+
+
+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);
+
+struct drm_plane *rockchip_plane_init(struct drm_device *dev,
+				      unsigned long possible_crtcs, bool priv);
+
+void rockchip_drm_encoder_setup(struct drm_device *dev);
+
+void *rockchip_drm_component_data_get(struct device *dev,
+				      enum rockchip_drm_device_type dev_type);
+int rockchip_drm_pipe_get(struct device *dev);
+
+int rockchip_drm_component_add(struct device *dev,
+			       enum rockchip_drm_device_type dev_type,
+			       int out_type, void *data);
+void rockchip_drm_component_del(struct device *dev,
+				enum rockchip_drm_device_type dev_type);
+
+extern struct platform_driver rockchip_panel_platform_driver;
+#ifdef CONFIG_DRM_ROCKCHIP_LCDC
+extern struct platform_driver rockchip_lcdc_platform_driver;
+#endif
+#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..a04024b
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fb.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 <drm/drmP.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fb_helper.h>
+
+#include <uapi/drm/rockchip_drm.h>
+
+static struct drm_framebuffer *
+rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+			struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	return drm_fb_cma_create(dev, file_priv, mode_cmd);
+}
+
+static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
+	.fb_create = rockchip_user_fb_create,
+};
+
+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..6258de6
--- /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>
+ *
+ * based on exynos_drm_fb.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_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_mode_config_init(struct drm_device *dev);
+
+#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..d32fa57
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fbdev.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 <drm/drmP.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+
+#define MAX_CONNECTOR		4
+#define PREFERRED_BPP		32
+
+int rockchip_drm_fbdev_init(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct drm_fbdev_cma *fbdev_cma;
+	unsigned int num_crtc;
+
+	if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
+		return 0;
+
+	if (private->fbdev_cma) {
+		DRM_ERROR("no allow to reinit cma fbdev\n");
+		return -EINVAL;
+	}
+
+	num_crtc = dev->mode_config.num_crtc;
+
+	fbdev_cma = drm_fbdev_cma_init(dev, PREFERRED_BPP, num_crtc,
+				       MAX_CONNECTOR);
+	if (!fbdev_cma) {
+		DRM_ERROR("failed to init cma fbdev\n");
+		return -ENOMEM;
+	}
+
+	private->fbdev_cma = fbdev_cma;
+
+	return 0;
+}
+
+void rockchip_drm_fbdev_fini(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+
+	if (!private || !private->fbdev_cma)
+		return;
+
+	drm_fbdev_cma_fini(private->fbdev_cma);
+}
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..91cb535
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fbdev.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_FBDEV_H_
+#define _ROCKCHIP_DRM_FBDEV_H_
+
+int rockchip_drm_fbdev_init(struct drm_device *dev);
+void rockchip_drm_fbdev_fini(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..f0219cd
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -0,0 +1,163 @@
+/*
+ *
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_gem.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 <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include <drm/rockchip_drm.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+int rockchip_drm_gem_create_ioctl(struct drm_device *dev, void *data,
+				  struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_create *args = data;
+	struct drm_gem_cma_object *cma_obj;
+	struct drm_gem_object *gem_obj;
+	int ret;
+
+	cma_obj = drm_gem_cma_create(dev, args->size);
+	if (IS_ERR(cma_obj))
+		return PTR_ERR_OR_ZERO(cma_obj);
+
+	gem_obj = &cma_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, gem_obj, &args->handle);
+	if (ret)
+		goto err_handle_create;
+
+	/* drop reference from allocate - handle holds it now. */
+	drm_gem_object_unreference_unlocked(gem_obj);
+
+	return PTR_ERR_OR_ZERO(cma_obj);
+
+err_handle_create:
+	drm_gem_cma_free_object(gem_obj);
+	return ret;
+}
+
+int rockchip_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+				      struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_map_off *args = data;
+
+	DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
+		      args->handle, (unsigned long)args->offset);
+
+	return drm_gem_cma_dumb_map_offset(file_priv, dev, args->handle,
+					   &args->offset);
+}
+
+int rockchip_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
+				struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_mmap *args = data;
+	struct drm_gem_object *obj;
+	unsigned long addr;
+
+	mutex_lock(&dev->struct_mutex);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		mutex_unlock(&dev->struct_mutex);
+		return -EINVAL;
+	}
+
+	addr = vm_mmap(obj->filp, 0, args->size, PROT_READ | PROT_WRITE,
+		       MAP_SHARED, 0);
+
+	drm_gem_object_unreference(obj);
+
+	if (IS_ERR_VALUE(addr)) {
+		mutex_unlock(&dev->struct_mutex);
+		return (int)addr;
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+
+	args->mapped = addr;
+
+	DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
+
+	return 0;
+}
+
+int rockchip_drm_gem_get_ioctl(struct drm_device *dev, void *data,
+			       struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_info *args = data;
+	struct drm_gem_object *obj;
+
+	mutex_lock(&dev->struct_mutex);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		mutex_unlock(&dev->struct_mutex);
+		return -EINVAL;
+	}
+
+	args->size = obj->size;
+
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+}
+
+int rockchip_drm_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;
+}
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..fe8285f
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
@@ -0,0 +1,40 @@
+/*
+ * 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_
+
+/*
+ * request gem object creation and buffer allocation as the size
+ * that it is calculated with framebuffer information such as width,
+ * height and bpp.
+ */
+int rockchip_drm_gem_create_ioctl(struct drm_device *dev, void *data,
+				  struct drm_file *file_priv);
+
+/* get buffer offset to map to user space. */
+int rockchip_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+				      struct drm_file *file_priv);
+
+/*
+ * mmap the physically continuous memory that a gem object contains
+ * to user space.
+ */
+int rockchip_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
+				struct drm_file *file_priv);
+
+/* get buffer information to memory region allocated by gem. */
+int rockchip_drm_gem_get_ioctl(struct drm_device *dev, void *data,
+			       struct drm_file *file_priv);
+#endif
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c b/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c
new file mode 100644
index 0000000..68991d4
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.c
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_fimd.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 <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_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 <drm/rockchip_drm.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_lcdc.h"
+
+#define LCDC_DEFAULT_FRAMERATE 60
+
+#define ROCKCHIP_DISPLAY_TYPE_LCD (ROCKCHIP_DISPLAY_TYPE_RGB | \
+					ROCKCHIP_DISPLAY_TYPE_LVDS | \
+					ROCKCHIP_DISPLAY_TYPE_EDP)
+
+static const uint32_t formats[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+};
+
+struct rockchip_plane {
+	int zpos;
+	struct drm_plane base;
+};
+
+struct lcdc_context {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_crtc crtc;
+	struct drm_pending_vblank_event *event;
+	struct drm_display_mode mode;
+	struct drm_plane *plane;
+	struct lcdc_driver *drv;
+	unsigned int default_win;
+	unsigned int dpms;
+	int pipe;
+	wait_queue_head_t wait_vsync_queue;
+	atomic_t wait_vsync_event;
+};
+
+#define to_lcdc_data(x) ((x)->drv->data)
+#define to_lcdc_ctx(x) container_of(x, struct lcdc_context, crtc)
+#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
+
+const struct of_device_id lcdc_driver_dt_match[] = {
+#ifdef CONFIG_LCDC_RK3288
+	{ .compatible = "rockchip,rk3288-lcdc",
+	  .data = (void *)&rockchip_rk3288_lcdc },
+#endif
+	{},
+};
+
+static inline struct lcdc_driver_data *drm_lcdc_get_driver_data(
+	struct platform_device *pdev)
+{
+	const struct of_device_id *of_id =
+			of_match_device(lcdc_driver_dt_match, &pdev->dev);
+
+	return (struct lcdc_driver_data *)of_id->data;
+}
+
+static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
+{
+	int end = start + length;
+	int size = 0;
+
+	if (start <= 0) {
+		if (end > 0)
+			size = min_t(unsigned, end, last);
+	} else if (start <= last) {
+		size = min_t(unsigned, last - start, length);
+	}
+
+	return size;
+}
+
+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);
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+	struct drm_gem_cma_object *gem;
+	struct lcdc_win_data *win_data;
+	unsigned long offset;
+	unsigned int actual_w;
+	unsigned int actual_h;
+	int win;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+	if (!gem) {
+		DRM_ERROR("fail to get cma object from framebuffer\n");
+		return -EINVAL;
+	}
+
+	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);
+	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;
+	}
+
+	win = rockchip_plane->zpos;
+	if (win == DEFAULT_ZPOS)
+		win = ctx->default_win;
+
+	if (win < 0 || win >= ZPOS_MAX_NUM)
+		return -EINVAL;
+
+	offset = (src_x >> 16) * (fb->bits_per_pixel >> 3);
+	offset += (src_y >> 16) * fb->pitches[0];
+
+	DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, fb->pitches[0]);
+
+	win_data = lcdc_data->get_win(ctx->drv, win);
+
+	win_data->xpos = crtc_x;
+	win_data->ypos = crtc_y;
+	win_data->xsize = actual_w;
+	win_data->ysize = actual_h;
+	win_data->xact = fb->width;
+	win_data->yact = fb->height;
+	win_data->y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3);
+	win_data->yrgb_addr = gem->paddr + offset;
+	win_data->uv_addr = 0;
+	win_data->alpha_en = false;
+
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_ARGB8888:
+		win_data->alpha_en = true;
+	case DRM_FORMAT_XRGB8888:
+		win_data->format = ARGB888;
+		break;
+	case DRM_FORMAT_RGB565:
+		win_data->format = RGB565;
+		win_data->y_vir_stride =
+			((win_data->y_vir_stride * 3) >> 2)
+			+ win_data->y_vir_stride % 3;
+		break;
+	default:
+		DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");
+		win_data->alpha_en = false;
+		win_data->format = ARGB888;
+		break;
+	}
+	win_data->enabled = true;
+	DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
+		      win_data->xpos, win_data->ypos);
+	DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
+		      win_data->xsize, win_data->ysize);
+	DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->yrgb_addr);
+	DRM_DEBUG_KMS("fb_width = %d, actual_w = %d\n",
+		      fb->width, actual_w);
+
+	lcdc_data->win_commit(ctx->drv, win_data);
+	return 0;
+}
+
+static int rockchip_disable_plane(struct drm_plane *plane)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	struct lcdc_context *ctx = to_lcdc_ctx(plane->crtc);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+	struct lcdc_win_data *win_data;
+	int win = rockchip_plane->zpos;
+
+	if (win == DEFAULT_ZPOS)
+		win = ctx->default_win;
+
+	if (win < 0 || win >= ZPOS_MAX_NUM)
+		return -EINVAL;
+
+	win_data = lcdc_data->get_win(ctx->drv, win);
+
+	win_data->enabled = false;
+	lcdc_data->win_commit(ctx->drv, win_data);
+
+	return 0;
+}
+
+static void rockchip_plane_destroy(struct drm_plane *plane)
+{
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	rockchip_disable_plane(plane);
+	drm_plane_cleanup(plane);
+	kfree(rockchip_plane);
+}
+
+static int rockchip_plane_set_property(struct drm_plane *plane,
+				       struct drm_property *property,
+				       uint64_t val)
+{
+	struct drm_device *dev = plane->dev;
+	struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+	struct rockchip_drm_private *dev_priv = dev->dev_private;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	if (property == dev_priv->plane_zpos_property) {
+		rockchip_plane->zpos = val;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static struct drm_plane_funcs rockchip_plane_funcs = {
+	.update_plane = rockchip_update_plane,
+	.disable_plane = rockchip_disable_plane,
+	.destroy = rockchip_plane_destroy,
+	.set_property = rockchip_plane_set_property,
+};
+
+static void rockchip_plane_attach_zpos_property(struct drm_plane *plane)
+{
+	struct drm_device *dev = plane->dev;
+	struct rockchip_drm_private *dev_priv = dev->dev_private;
+	struct drm_property *prop;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	prop = dev_priv->plane_zpos_property;
+	if (!prop) {
+		prop = drm_property_create_range(dev, 0, "zpos", 0,
+						 MAX_PLANE - 1);
+		if (!prop)
+			return;
+
+		dev_priv->plane_zpos_property = prop;
+	}
+
+	drm_object_attach_property(&plane->base, prop, 0);
+}
+
+struct drm_plane *rockchip_plane_init(struct drm_device *dev,
+				      unsigned long possible_crtcs, bool priv)
+{
+	struct rockchip_plane *rockchip_plane;
+	struct rockchip_drm_private *private = dev->dev_private;
+	int err;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
+	if (!rockchip_plane)
+		return NULL;
+
+	err = drm_plane_init(dev, &rockchip_plane->base, possible_crtcs,
+			     &rockchip_plane_funcs, formats,
+			     ARRAY_SIZE(formats), priv);
+	if (err) {
+		DRM_ERROR("failed to initialize plane\n");
+		kfree(rockchip_plane);
+		return NULL;
+	}
+
+	if (priv) {
+		rockchip_plane->base.crtc = private->crtc[0];
+		rockchip_plane->zpos = DEFAULT_ZPOS;
+	} else {
+		rockchip_plane_attach_zpos_property(&rockchip_plane->base);
+	}
+
+	return &rockchip_plane->base;
+}
+
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct lcdc_context *ctx = to_lcdc_ctx(private->crtc[pipe]);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	if (ctx->dpms != DRM_MODE_DPMS_ON)
+		return -EPERM;
+
+	lcdc_data->enable_vblank(ctx->drv);
+
+	return 0;
+}
+
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct lcdc_context *ctx = to_lcdc_ctx(private->crtc[pipe]);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	if (ctx->dpms != DRM_MODE_DPMS_ON)
+		return;
+
+	lcdc_data->disable_vblank(ctx->drv);
+}
+
+static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+
+	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:
+		lcdc_data->dpms(ctx->drv, DRM_MODE_DPMS_ON);
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		lcdc_data->dpms(ctx->drv, DRM_MODE_DPMS_OFF);
+		break;
+	default:
+		DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+		break;
+	}
+
+	ctx->dpms = mode;
+}
+
+static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+	/* drm framework doesn't check NULL. */
+}
+
+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(struct drm_crtc *crtc,
+				      struct drm_display_mode *mode,
+				      struct drm_display_mode *adjusted_mode,
+				      int x, int y,
+				      struct drm_framebuffer *fb)
+{
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct lcdc_driver_data *lcdc_data = to_lcdc_data(ctx);
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	/* nothing to do if we haven't set the mode yet */
+	if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
+		return -EINVAL;
+
+	drm_mode_copy(&ctx->mode, adjusted_mode);
+	lcdc_data->mode_set(ctx->drv, &ctx->mode);
+
+	return 0;
+}
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+					   struct drm_framebuffer *old_fb)
+{
+	struct lcdc_context *ctx = to_lcdc_ctx(crtc);
+	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(ctx->plane, crtc, crtc->primary->fb, 0, 0,
+				    crtc_w, crtc_h, crtc->x, crtc->y, crtc_w,
+				    crtc_h);
+	if (ret < 0) {
+		DRM_ERROR("fail to update plane\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
+{
+	rockchip_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y,
+					crtc->primary->fb);
+}
+
+static 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 lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct drm_framebuffer *old_fb = crtc->primary->fb;
+	unsigned int crtc_w;
+	unsigned int crtc_h;
+	int ret = -EINVAL;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	/* when the page flip is requested, crtc's dpms should be on */
+	if (ctx->dpms > DRM_MODE_DPMS_ON) {
+		DRM_ERROR("failed page flip request.\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+
+	/*
+	 * the pipe from user always is 0 so we can set pipe number
+	 * of current owner to event.
+	 */
+	ret = drm_vblank_get(dev, ctx->pipe);
+	if (ret) {
+		DRM_DEBUG("failed to acquire vblank counter\n");
+		goto out;
+	}
+
+	spin_lock_irq(&dev->event_lock);
+	if (ctx->event) {
+		spin_unlock_irq(&dev->event_lock);
+		DRM_ERROR("already pending flip!\n");
+		ret = -EBUSY;
+		goto out;
+	}
+	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(ctx->plane, 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);
+
+		goto out;
+	}
+out:
+	mutex_unlock(&dev->struct_mutex);
+	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 lcdc_context *ctx;
+	struct drm_pending_vblank_event *event;
+	unsigned long flags;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	if (!drm_crtc)
+		return;
+
+	ctx = to_lcdc_ctx(drm_crtc);
+	event = ctx->event;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	if (event) {
+		ctx->event = NULL;
+		drm_send_vblank_event(dev, -1, event);
+		drm_vblank_put(dev, pipe);
+		atomic_set(&ctx->wait_vsync_event, 0);
+		wake_up(&ctx->wait_vsync_queue);
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev)
+{
+	int i;
+
+	DRM_DEBUG_KMS("cancle pending flip\n");
+	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 lcdc_context *ctx = to_lcdc_ctx(crtc);
+	struct rockchip_drm_private *private = crtc->dev->dev_private;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	private->crtc[ctx->pipe] = NULL;
+
+	drm_crtc_cleanup(crtc);
+}
+
+static 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,
+};
+
+void lcdc_vsync_event_handler(struct device *dev)
+{
+	struct drm_pending_vblank_event *event;
+	struct lcdc_context *ctx = dev_get_drvdata(dev);
+	struct drm_device *drm_dev;
+	unsigned long flags;
+
+	DRM_DEBUG_KMS("LINE[%d]\n", __LINE__);
+	/* check the crtc is detached already from encoder */
+	if (ctx && (ctx->pipe < 0 || !ctx->drm_dev))
+		return;
+
+	drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+
+	event = ctx->event;
+	drm_dev = ctx->drm_dev;
+
+	spin_lock_irqsave(&drm_dev->event_lock, flags);
+
+	if (event) {
+		ctx->event = NULL;
+		drm_send_vblank_event(drm_dev, -1, event);
+		drm_vblank_put(drm_dev, ctx->pipe);
+		atomic_set(&ctx->wait_vsync_event, 0);
+		wake_up(&ctx->wait_vsync_queue);
+	}
+
+	spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+}
+
+static int lcdc_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm_dev = data;
+	struct rockchip_drm_private *private = drm_dev->dev_private;
+	struct lcdc_context *ctx = dev_get_drvdata(dev);
+	struct drm_crtc *crtc;
+
+	ctx->drm_dev = drm_dev;
+
+	ctx->pipe = rockchip_drm_pipe_get(dev);
+	ctx->dpms = DRM_MODE_DPMS_OFF;
+	crtc = &ctx->crtc;
+
+	private->crtc[ctx->pipe] = crtc;
+	ctx->plane = rockchip_plane_init(drm_dev, 1 << ctx->pipe, true);
+	drm_crtc_init(drm_dev, crtc, &rockchip_crtc_funcs);
+	drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
+
+	/*
+	 * enable drm irq mode.
+	 * - with irq_enabled = true, we can use the vblank feature.
+	 *
+	 * P.S. note that we wouldn't use drm irq handler but
+	 *      just specific driver own one instead because
+	 *      drm framework supports only one irq handler.
+	 */
+	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;
+
+	return 0;
+}
+
+static void lcdc_unbind(struct device *dev, struct device *master,
+			void *data)
+{	struct drm_device *drm_dev = data;
+	struct rockchip_drm_private *private = drm_dev->dev_private;
+	struct lcdc_context *ctx = dev_get_drvdata(dev);
+	struct drm_crtc *crtc = &ctx->crtc;
+
+	drm_crtc_cleanup(crtc);
+	private->crtc[ctx->pipe] = NULL;
+}
+
+static const struct component_ops lcdc_component_ops = {
+	.bind = lcdc_bind,
+	.unbind = lcdc_unbind,
+};
+
+static int lcdc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct lcdc_context *ctx;
+	struct lcdc_driver *lcdc_drv;
+	struct lcdc_driver_data *lcdc_data = drm_lcdc_get_driver_data(pdev);
+	int ret = -EINVAL;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	if (!(lcdc_data->num_win && lcdc_data->init &&
+	      lcdc_data->deinit && lcdc_data->dpms &&
+	      lcdc_data->mode_set && lcdc_data->enable_vblank &&
+	      lcdc_data->disable_vblank &&
+	      lcdc_data->win_commit)) {
+		DRM_ERROR("lcdc driver ops is Incomplete\n");
+		return -EINVAL;
+	}
+	lcdc_drv = lcdc_data->init(pdev);
+	if (!lcdc_drv)
+		return -EINVAL;
+
+	lcdc_drv->data = lcdc_data;
+	ret = rockchip_drm_component_add(&pdev->dev, ROCKCHIP_DEVICE_TYPE_CRTC,
+					 ROCKCHIP_DISPLAY_TYPE_LCD, ctx);
+	if (ret)
+		goto err_deinit_lcdc;
+
+	ctx->dev = dev;
+	ctx->default_win = ZPOS_DEFAULT_WIN;
+
+	ctx->drv = lcdc_drv;
+
+	init_waitqueue_head(&ctx->wait_vsync_queue);
+	atomic_set(&ctx->wait_vsync_event, 0);
+
+	platform_set_drvdata(pdev, ctx);
+
+	pm_runtime_enable(&pdev->dev);
+
+	ret = component_add(&pdev->dev, &lcdc_component_ops);
+	if (ret)
+		goto err_disable_pm_runtime;
+
+	return ret;
+
+err_disable_pm_runtime:
+	pm_runtime_disable(dev);
+	rockchip_drm_component_del(dev, ROCKCHIP_DEVICE_TYPE_CRTC);
+err_deinit_lcdc:
+	lcdc_data->deinit(ctx->drv);
+	return ret;
+}
+
+static int lcdc_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+
+	component_del(&pdev->dev, &lcdc_component_ops);
+	rockchip_drm_component_del(&pdev->dev, ROCKCHIP_DEVICE_TYPE_CRTC);
+
+	return 0;
+}
+
+struct platform_driver rockchip_lcdc_platform_driver = {
+	.probe = lcdc_probe,
+	.remove = lcdc_remove,
+	.driver = {
+		.name = "rockchip-lcdc",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(lcdc_driver_dt_match),
+	},
+};
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h b/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h
new file mode 100644
index 0000000..0a0f9c2
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_lcdc.h
@@ -0,0 +1,131 @@
+/*
+ * 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_LCDC_H_
+#define _ROCKCHIP_DRM_LCDC_H_
+#include <linux/platform_device.h>
+#include <drm/drm_crtc.h>
+
+#include "rockchip_drm_drv.h"
+
+enum {
+	ZPOS_DEFAULT_WIN = 0,
+	ZPOS_CURSOR_WIN,
+	ZPOS_MAX_NUM,
+	ZPOS_UNUSED_WIN
+};
+
+enum data_format {
+	ARGB888 = 0,
+	RGB888,
+	RGB565,
+	YUV420 = 4,
+	YUV422,
+	YUV444,
+	XRGB888,
+	XBGR888,
+	ABGR888,
+	YUV420_A = 10,
+	YUV422_A,
+	YUV444_A
+};
+
+struct lcdc_win_data {
+	int zpos;
+	int id;
+	enum data_format format;
+	u32 xact;
+	u32 yact;
+	u32 xsize;
+	u32 ysize;
+	u32 xpos;
+	u32 ypos;
+	u32 y_vir_stride;
+	u32 uv_vir_stride;
+	bool alpha_en;
+	bool enabled;
+	bool resume;
+
+	dma_addr_t yrgb_addr;
+	dma_addr_t uv_addr;
+
+	int dsp_stx;
+	int dsp_sty;
+	/* win sel layer */
+	int z_order;
+	u8 fmt_cfg;
+	u8 fmt_10;
+	u8 swap_rb;
+	u32 reserved;
+	u32 area_num;
+	u32 scale_yrgb_x;
+	u32 scale_yrgb_y;
+	u32 scale_cbcr_x;
+	u32 scale_cbcr_y;
+	bool support_3d;
+
+	u8 win_lb_mode;
+
+	u8 bic_coe_el;
+	/* h 01:scale up ;10:down */
+	u8 yrgb_hor_scl_mode;
+	/* v 01:scale up ;10:down */
+	u8 yrgb_ver_scl_mode;
+	/* h scale down mode */
+	u8 yrgb_hsd_mode;
+	/* v scale up mode */
+	u8 yrgb_vsu_mode;
+	/* v scale down mode */
+	u8 yrgb_vsd_mode;
+	u8 cbr_hor_scl_mode;
+	u8 cbr_ver_scl_mode;
+	u8 cbr_hsd_mode;
+	u8 cbr_vsu_mode;
+	u8 cbr_vsd_mode;
+	u8 vsd_yrgb_gt4;
+	u8 vsd_yrgb_gt2;
+	u8 vsd_cbr_gt4;
+	u8 vsd_cbr_gt2;
+
+	u32 alpha_mode;
+	u32 g_alpha_val;
+	u32 color_key_val;
+};
+
+struct lcdc_driver_data {
+	int num_win;
+	struct lcdc_driver * (*init)(struct platform_device *pdev);
+	void (*deinit)(struct lcdc_driver *drv);
+	void (*dpms)(struct lcdc_driver *drv, int mode);
+	void (*mode_set)(struct lcdc_driver *drv,
+			 struct drm_display_mode *mode);
+	void (*enable_vblank)(struct lcdc_driver *drv);
+	void (*disable_vblank)(struct lcdc_driver *drv);
+	struct lcdc_win_data * (*get_win)(struct lcdc_driver *drv, int zpos);
+	void (*win_commit)(struct lcdc_driver *drv,
+			   struct lcdc_win_data *win);
+};
+
+struct lcdc_driver {
+	int id;
+
+	struct lcdc_driver_data *data;
+};
+
+
+void lcdc_vsync_event_handler(struct device *dev);
+#ifdef CONFIG_LCDC_RK3288
+extern struct lcdc_driver_data rockchip_rk3288_lcdc;
+#endif
+#endif /* _ROCKCHIP_DRM_LCDC_H_ */
diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h
new file mode 100644
index 0000000..1b567f7
--- /dev/null
+++ b/include/uapi/drm/rockchip_drm.h
@@ -0,0 +1,110 @@
+/*
+ *
+ * Copyright (c) Fuzhou Rockchip Electronics Co.Ltd
+ * Authors:
+ *       mark yao <yzq@rock-chips.com>
+ *
+ * base on exynos_drm.h
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _UAPI_ROCKCHIP_DRM_H_
+#define _UAPI_ROCKCHIP_DRM_H_
+
+#include <drm/drm.h>
+
+/**
+ * User-desired buffer creation information structure.
+ *
+ * @size: user-desired memory allocation size.
+ *     - this size value would be page-aligned internally.
+ * @flags: user request for setting memory type or cache attributes.
+ * @handle: returned a handle to created gem object.
+ *     - this handle will be set by gem module of kernel side.
+ */
+struct drm_rockchip_gem_create {
+	uint64_t size;
+	unsigned int flags;
+	unsigned int handle;
+};
+
+/**
+ * A structure for getting buffer offset.
+ *
+ * @handle: a pointer to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @offset: relatived offset value of the memory region allocated.
+ *     - this value should be set by user.
+ */
+struct drm_rockchip_gem_map_off {
+	unsigned int handle;
+	unsigned int pad;
+	uint64_t offset;
+};
+
+/**
+ * A structure for mapping buffer.
+ *
+ * @handle: a handle to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @size: memory size to be mapped.
+ * @mapped: having user virtual address mmaped.
+ *      - this variable would be filled by rockchip gem module
+ *      of kernel side with user virtual address which is allocated
+ *      by do_mmap().
+ */
+struct drm_rockchip_gem_mmap {
+	unsigned int handle;
+	unsigned int pad;
+	uint64_t size;
+	uint64_t mapped;
+};
+
+/**
+ * A structure to gem information.
+ *
+ * @handle: a handle to gem object created.
+ * @flags: flag value including memory type and cache attribute and
+ *      this value would be set by driver.
+ * @size: size to memory region allocated by gem and this size would
+ *      be set by driver.
+ */
+struct drm_rockchip_gem_info {
+	unsigned int handle;
+	unsigned int flags;
+	uint64_t size;
+};
+
+/* memory type definitions. */
+enum e_drm_rockchip_gem_mem_type {
+	/* non-cachable mapping and used as default. */
+	ROCKCHIP_BO_NONCACHABLE = 0 << 0,
+	/* cachable mapping. */
+	ROCKCHIP_BO_CACHABLE = 1 << 0,
+	/* write-combine mapping. */
+	ROCKCHIP_BO_WC = 1 << 1,
+	ROCKCHIP_BO_MASK = ROCKCHIP_BO_CACHABLE | ROCKCHIP_BO_WC
+};
+
+#define DRM_ROCKCHIP_GEM_CREATE		0x00
+#define DRM_ROCKCHIP_GEM_MAP_OFFSET	0x01
+#define DRM_ROCKCHIP_GEM_MMAP		0x02
+/* Reserved 0x03 ~ 0x05 for rockchip specific gem ioctl */
+#define DRM_ROCKCHIP_GEM_GET		0x04
+
+#define DRM_IOCTL_ROCKCHIP_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_MMAP, struct drm_rockchip_gem_mmap)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_GET	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_GET, struct drm_rockchip_gem_info)
+#endif /* _UAPI_ROCKCHIP_DRM_H_ */
-- 
1.7.9.5



^ permalink raw reply related

* [PATCH 2/9] Add devicetree bindings for panels used by the Rockchip DRM
From: mark yao @ 2014-08-04  4:47 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao
In-Reply-To: <1407127274-1356-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 .../devicetree/bindings/video/rockchip-panel.txt   |   52 ++++++++++++++++++++
 1 file changed, 52 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-panel.txt

diff --git a/Documentation/devicetree/bindings/video/rockchip-panel.txt b/Documentation/devicetree/bindings/video/rockchip-panel.txt
new file mode 100644
index 0000000..9fc200a
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-panel.txt
@@ -0,0 +1,52 @@
+Rockchip LCD Display interface
+================================
+Required properties:
+-compatible: "rockchip,rockchip-panel";
+
+- rockchip,output-face: lcdc display mode value should set as following
+		"r8g8b8": 24bit display port, R8 G8 B8
+		"r6g6b6": 18bit display port, R6 G6 B6
+		"r5g6b5": 16bit display port, R5 G6 B5
+
+Optional properties:
+- display-timings: timings for the connected panel as described by
+	Documentation/devicetree/bindings/video/display-timing.txt
+Optional properties:
+- rockchip,power-gpios: gpio to power on or off the LCD (as many as needed)
+- lcdc-vcc18: Defined if power supply of lcd controller is 1.8v,
+		Not defined if power supply of lcd controller is 3.3v.
+- output-dither: support dithering the output color.
+- color-swap-rb: swap R and B color per pixel.
+- color-swap-rg: swap R and G color per pixel.
+- color-swap-bg: swap B and G color per pixel.
+
+Example:
+	panel {
+		compatible = "rockchip,rockchip-panel";
+		rockchip,output-face = "r6g6b6";
+
+		enable-gpios = <&gpio7 3 GPIO_ACTIVE_HIGH
+				&gpio7 4 GPIO_ACTIVE_HIGH>;
+		output-dither;
+
+		display-timings {
+			native-mode = <&timing_disp0>;
+
+			timing_disp0: 1280x800 {
+				clock-frequency = <71000000>;
+				hactive = <1280>;
+				vactive = <800>;
+				hback-porch = <100>;
+				hfront-porch = <18>;
+				vback-porch = <8>;
+				vfront-porch = <6>;
+				hsync-len = <10>;
+				vsync-len = <2>;
+				hsync-active = <0>;
+				vsync-active = <0>;
+				de-active = <0>;
+				pixelclk-active = <0>;
+			};
+		};
+
+	};
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 3/9] drm: add driver for panels used by the Rockchip DRM
From: mark yao @ 2014-08-04  4:48 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao
In-Reply-To: <1407127274-1356-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 drivers/gpu/drm/rockchip/Makefile         |    3 +-
 drivers/gpu/drm/rockchip/rockchip_panel.c |  297 +++++++++++++++++++++++++++++
 2 files changed, 299 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_panel.c

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 45c9d50..a5e5132 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -5,7 +5,8 @@
 ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_gem.o \
-		rockchip_drm_fb.o rockchip_drm_fbdev.o
+		rockchip_drm_fb.o rockchip_drm_fbdev.o \
+		rockchip_panel.o
 
 obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o
 obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_panel.c b/drivers/gpu/drm/rockchip/rockchip_panel.c
new file mode 100644
index 0000000..87401a2
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_panel.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * based on panel-simple.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 <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_panel.h>
+
+#include "rockchip_drm_drv.h"
+
+/* TODO: convert to gpiod_*() API once it's been merged */
+#define GPIO_ACTIVE_LOW	(1 << 0)
+
+struct pwr_gpio {
+	struct list_head head;
+	unsigned long enable_gpio_flags;
+	int enable_gpio;
+};
+
+struct rockchip_panel {
+	struct drm_panel base;
+	bool enabled;
+
+	struct drm_display_mode mode;
+	struct rockchip_panel_special priv;
+
+	struct list_head pwrlist;
+};
+
+static inline struct rockchip_panel *to_rockchip_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct rockchip_panel, base);
+}
+
+static int rockchip_panel_disable(struct drm_panel *panel)
+{
+	struct rockchip_panel *p = to_rockchip_panel(panel);
+	struct pwr_gpio *pwr;
+	struct list_head *pos;
+
+	if (!p->enabled)
+		return 0;
+
+	list_for_each(pos, &p->pwrlist) {
+		pwr = list_entry(pos, struct pwr_gpio, head);
+		if (gpio_is_valid(pwr->enable_gpio)) {
+			if (pwr->enable_gpio_flags & GPIO_ACTIVE_LOW)
+				gpio_set_value(pwr->enable_gpio, 1);
+			else
+				gpio_set_value(pwr->enable_gpio, 0);
+		}
+	}
+
+	p->enabled = false;
+
+	return 0;
+}
+
+static int rockchip_panel_enable(struct drm_panel *panel)
+{
+	struct rockchip_panel *p = to_rockchip_panel(panel);
+	struct pwr_gpio *pwr;
+	struct list_head *pos;
+
+	if (p->enabled)
+		return 0;
+
+	list_for_each(pos, &p->pwrlist) {
+		pwr = list_entry(pos, struct pwr_gpio, head);
+		if (gpio_is_valid(pwr->enable_gpio)) {
+			if (pwr->enable_gpio_flags & GPIO_ACTIVE_LOW)
+				gpio_set_value(pwr->enable_gpio, 0);
+			else
+				gpio_set_value(pwr->enable_gpio, 1);
+		}
+	}
+
+	p->enabled = true;
+
+	return 0;
+}
+
+static int rockchip_panel_get_modes(struct drm_panel *panel)
+{
+	struct rockchip_panel *p = to_rockchip_panel(panel);
+	struct drm_device *drm = panel->drm;
+	struct drm_connector *connector = panel->connector;
+	const struct drm_display_mode *m = &p->mode;
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(drm, m);
+	if (!mode) {
+		dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
+			m->hdisplay, m->vdisplay, m->vrefresh);
+		return 0;
+	}
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	return 1;
+}
+
+static const struct drm_panel_funcs rockchip_panel_funcs = {
+	.disable = rockchip_panel_disable,
+	.enable = rockchip_panel_enable,
+	.get_modes = rockchip_panel_get_modes,
+};
+
+static int rockchip_name_to_face(const char *s)
+{
+	if (!s)
+		return 0;
+
+	if (strncmp(s, "r8g8b8", 6) == 0)
+		return ROCKCHIP_OUTFACE_P888;
+	else if (strncmp(s, "r6g6b6", 6) == 0)
+		return ROCKCHIP_OUTFACE_P666;
+	else if (strncmp(s, "r5g6b5", 6) == 0)
+		return ROCKCHIP_OUTFACE_P565;
+
+	DRM_ERROR("unsupport display output face[%s]\n", s);
+
+	return 0;
+}
+
+static int rockchip_panel_probe(struct platform_device *pdev)
+{
+	struct rockchip_panel *panel;
+	struct device *dev = &pdev->dev;
+	struct rockchip_panel_special *priv;
+	struct device_node *dn = dev->of_node;
+	struct device_node *np;
+	enum of_gpio_flags flags;
+	struct videomode vm;
+	const char *name;
+	struct pwr_gpio *pwr;
+	struct list_head *pos;
+	int value;
+	int err, i;
+	int num_gpio;
+
+	panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
+	if (!panel)
+		return -ENOMEM;
+
+	priv = &panel->priv;
+
+	INIT_LIST_HEAD(&panel->pwrlist);
+	num_gpio = of_gpio_named_count(dn, "enable-gpios");
+	for (i = 0; i < num_gpio; i++) {
+		pwr = kmalloc(sizeof(*pwr), GFP_KERNEL);
+		pwr->enable_gpio = of_get_named_gpio_flags(dn,
+							   "enable-gpios", i,
+							   &flags);
+		if (flags & OF_GPIO_ACTIVE_LOW)
+			pwr->enable_gpio_flags |= GPIO_ACTIVE_LOW;
+
+		if (gpio_is_valid(pwr->enable_gpio)) {
+			err = gpio_request(pwr->enable_gpio, NULL);
+			if (err < 0) {
+				dev_err(dev, "failed to request GPIO#%u: %d\n",
+					pwr->enable_gpio, err);
+				gpio_free(pwr->enable_gpio);
+				kfree(pwr);
+				continue;
+			}
+			value = (pwr->enable_gpio_flags & GPIO_ACTIVE_LOW) != 0;
+			err = gpio_direction_output(pwr->enable_gpio, value);
+			if (err < 0) {
+				dev_err(dev, "failed to setup GPIO%u: %d\n",
+					pwr->enable_gpio, err);
+				gpio_free(pwr->enable_gpio);
+				kfree(pwr);
+				continue;
+			}
+
+			list_add_tail(&pwr->head, &panel->pwrlist);
+		}
+	}
+
+	if (of_property_read_bool(dn, "color-swap-rb"))
+		priv->color_swap = ROCKCHIP_COLOR_SWAP_RB;
+
+	if (of_property_read_bool(dn, "color-swap-rg"))
+		priv->color_swap |= ROCKCHIP_COLOR_SWAP_RG;
+
+	if (of_property_read_bool(dn, "color-swap-gb"))
+		priv->color_swap |= ROCKCHIP_COLOR_SWAP_GB;
+
+	if (of_property_read_string(dn, "rockchip,output-face", &name))
+		/* default set it as RGB screen */
+		priv->out_face = ROCKCHIP_OUTFACE_P666;
+	else
+		priv->out_face = rockchip_name_to_face(name);
+
+	priv->pwr18 = of_property_read_bool(dn, "lcd-vcc18");
+	priv->dither = of_property_read_bool(dn, "output-dither");
+
+	np = of_get_child_by_name(dn, "display-timings");
+	if (!np) {
+		DRM_ERROR("can't find display timings\n");
+		return 0;
+	}
+
+	of_node_put(np);
+	memset(&vm, 0, sizeof(vm));
+
+	err = of_get_videomode(dn, &vm, 0);
+	if (err < 0)
+		return err;
+
+	drm_display_mode_from_videomode(&vm, &panel->mode);
+	panel->mode.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	priv->flags = vm.flags;
+	panel->mode.private = (void *)priv;
+
+	drm_panel_init(&panel->base);
+	panel->base.dev = dev;
+	panel->base.funcs = &rockchip_panel_funcs;
+
+	err = drm_panel_add(&panel->base);
+	if (err < 0)
+		goto free_gpio;
+
+	dev_set_drvdata(dev, panel);
+
+	return 0;
+
+free_gpio:
+	list_for_each(pos, &panel->pwrlist) {
+		pwr = list_entry(pos, struct pwr_gpio, head);
+		if (gpio_is_valid(pwr->enable_gpio))
+			gpio_free(pwr->enable_gpio);
+		kfree(pwr);
+	}
+	return err;
+}
+
+static const struct of_device_id platform_of_match[] = {
+	{
+		.compatible = "rockchip,panel",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, platform_of_match);
+
+static int rockchip_panel_remove(struct platform_device *pdev)
+{
+	struct rockchip_panel *panel = dev_get_drvdata(&pdev->dev);
+	struct pwr_gpio *pwr;
+	struct list_head *pos;
+
+	drm_panel_detach(&panel->base);
+	drm_panel_remove(&panel->base);
+
+	list_for_each(pos, &panel->pwrlist) {
+		pwr = list_entry(pos, struct pwr_gpio, head);
+		if (gpio_is_valid(pwr->enable_gpio))
+			gpio_free(pwr->enable_gpio);
+		kfree(pwr);
+	}
+
+	return 0;
+}
+
+struct platform_driver rockchip_panel_platform_driver = {
+	.driver = {
+		.name = "rockchip,panel",
+		.owner = THIS_MODULE,
+		.of_match_table = platform_of_match,
+	},
+	.probe = rockchip_panel_probe,
+	.remove = rockchip_panel_remove,
+};
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 4/9] Add devicetree bindings for Rockchip lcd controller
From: mark yao @ 2014-08-04  4:50 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao
In-Reply-To: <1407127274-1356-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 .../devicetree/bindings/video/rockchip-lcdc.txt    |   33 ++++++++++++++++++++
 1 file changed, 33 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-lcdc.txt

diff --git a/Documentation/devicetree/bindings/video/rockchip-lcdc.txt b/Documentation/devicetree/bindings/video/rockchip-lcdc.txt
new file mode 100644
index 0000000..7fb2b73
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-lcdc.txt
@@ -0,0 +1,33 @@
+Device-Tree bindings for Rockchip SoC display controller (LCDC)
+
+LCDC (LCD Controller) is the Display Controller for the Rockchip
+series of SoCs which transfers the image data from a video memory
+buffer to an external LCD interface.
+
+Required properties:
+- compatible: value should be one of the following
+		"rockchip,rk3066-lcdc";
+		"rockchip,rk3188-lcdc";
+		"rockchip,rk3288-lcdc";
+
+- interrupts: should contain a list of all LCDC IP block interrupts in the
+		 order: VSYNC, LCD_SYSTEM. The interrupt specifier
+		 format depends on the interrupt controller used.
+
+- clocks: must include clock specifiers corresponding to entries in the
+		clock-names property.
+
+- clock-names: Must contain
+		aclk_lcdc: for ddr buffer transfer.
+		hclk_lcdc: for ahb bus to R/W the phy regs.
+		dclk_lcdc: pixel clock.
+
+Example:
+SoC specific DT entry:
+	lcdc1: lcdc@ff940000 {
+		compatible = "rockchip,rk3288-lcdc";
+		reg = <0xff940000 0x19c>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru ACLK_LCDC1>, <&cru DCLK_LCDC1>, <&cru HCLK_LCDC1>;
+		clock-names = "aclk_lcdc", "dclk_lcdc", "hclk_lcdc";
+	};
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 5/9] drm: add Rockchip rk3288 lcd controller driver
From: mark yao @ 2014-08-04  4:51 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao
In-Reply-To: <1407127274-1356-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 drivers/gpu/drm/rockchip/Kconfig            |    2 +
 drivers/gpu/drm/rockchip/Makefile           |    2 +-
 drivers/gpu/drm/rockchip/lcdc/Kconfig       |    9 +
 drivers/gpu/drm/rockchip/lcdc/Makefile      |    4 +
 drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c |  819 ++++++++++++++++++
 drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h | 1202 +++++++++++++++++++++++++++
 6 files changed, 2037 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/lcdc/Kconfig
 create mode 100644 drivers/gpu/drm/rockchip/lcdc/Makefile
 create mode 100644 drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c
 create mode 100644 drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 592e999..ccce827 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -38,3 +38,5 @@ config DRM_ROCKCHIP_CONNECTOR
 	  Choose this option if you want to use Rockchip Primary DISPLAY.
 	  The driver provides an abstraction for Rockchip display devices,
 	  such as lcd plane, lvds, edp , mipi, etc.
+
+source "drivers/gpu/drm/rockchip/lcdc/Kconfig"
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a5e5132..6d49edc 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -9,5 +9,5 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_gem.o \
 		rockchip_panel.o
 
 obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o
-obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o
+obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o lcdc/
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/lcdc/Kconfig b/drivers/gpu/drm/rockchip/lcdc/Kconfig
new file mode 100644
index 0000000..2b94057
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/lcdc/Kconfig
@@ -0,0 +1,9 @@
+config LCDC_RK3288
+        bool "rk3288 lcdc support"
+        depends on DRM_ROCKCHIP_LCDC
+        help
+	  Choose this option if you have a rk3288 lcd controller.
+	  rk3288 lcdc is the display interface from memory frame buffer
+	  to display device. There are two lcd controllers on rk3288,
+	  They have same regs setting, can use same drivers. We use the
+	  lcdc id distinguish between them
diff --git a/drivers/gpu/drm/rockchip/lcdc/Makefile b/drivers/gpu/drm/rockchip/lcdc/Makefile
new file mode 100644
index 0000000..943dcd6
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/lcdc/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for the lcd control device driver.
+
+obj-$(CONFIG_LCDC_RK3288) += rk3288_lcdc.o
diff --git a/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c b/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c
new file mode 100644
index 0000000..f1b016c
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.c
@@ -0,0 +1,819 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *      mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * 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/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <video/display_timing.h>
+#include <drm/rockchip_drm.h>
+#include <drm/drm_crtc.h>
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#endif
+
+#include "../rockchip_drm_lcdc.h"
+#include "rk3288_lcdc.h"
+
+#define RK3288_LCDC_BIG_BASE 0xff930000
+#define RK3288_LCDC_LIT_BASE 0xff940000
+
+static struct lcdc_win_data lcdc_win[] = {
+	[0] = {
+		.id = 0,
+		.zpos = ZPOS_DEFAULT_WIN,
+		.fmt_10 = 0,
+		.win_lb_mode = 0x4,
+		.swap_rb = 0,
+	},
+	[1] = {
+		.id = 1,
+		.zpos = ZPOS_UNUSED_WIN,
+		.fmt_10 = 0,
+		.win_lb_mode = 0x4,
+		.swap_rb = 0,
+	},
+	[2] = {
+		.id = 2,
+		.zpos = ZPOS_CURSOR_WIN,
+		.fmt_10 = 0,
+		.win_lb_mode = 0x4,
+		.swap_rb = 0,
+	},
+	[3] = {
+		.id = 3,
+		.zpos = ZPOS_UNUSED_WIN,
+	},
+};
+
+static void rk3288_lcdc_win_commit(struct lcdc_driver *lcdc_drv,
+				   struct lcdc_win_data *win);
+static void rk3288_lcdc_dpms(struct lcdc_driver *lcdc_drv, int mode);
+
+static int rk3288_lcdc_get_id(u32 phy_base)
+{
+	if (phy_base == RK3288_LCDC_BIG_BASE)
+		return 0;
+	else if (phy_base == RK3288_LCDC_LIT_BASE)
+		return 1;
+	else
+		return -EINVAL;
+}
+
+static int rk3288_lcdc_clk_enable(struct lcdc_device *lcdc_dev)
+{
+	int ret = 0;
+
+	if (!lcdc_dev->clk_on) {
+		ret = clk_prepare_enable(lcdc_dev->hclk);
+		if (ret < 0) {
+			dev_err(lcdc_dev->dev, "failed to enable hclk\n");
+			return ret;
+		}
+
+		ret = clk_prepare_enable(lcdc_dev->dclk);
+		if (ret < 0) {
+			dev_err(lcdc_dev->dev, "failed to enable dclk\n");
+			goto err_dclk;
+		}
+
+		clk_prepare_enable(lcdc_dev->aclk);
+		if (ret < 0) {
+			dev_err(lcdc_dev->dev, "failed to enable aclk\n");
+			goto err_aclk;
+		}
+
+		spin_lock(&lcdc_dev->reg_lock);
+		lcdc_dev->clk_on = 1;
+		spin_unlock(&lcdc_dev->reg_lock);
+	}
+
+	return ret;
+err_aclk:
+	clk_disable_unprepare(lcdc_dev->aclk);
+err_dclk:
+	clk_disable_unprepare(lcdc_dev->hclk);
+	return ret;
+}
+
+static void rk3288_lcdc_clk_disable(struct lcdc_device *lcdc_dev)
+{
+	if (lcdc_dev->clk_on) {
+		spin_lock(&lcdc_dev->reg_lock);
+		lcdc_dev->clk_on = 0;
+		spin_unlock(&lcdc_dev->reg_lock);
+		mdelay(25);
+		clk_disable_unprepare(lcdc_dev->dclk);
+		clk_disable_unprepare(lcdc_dev->hclk);
+		clk_disable_unprepare(lcdc_dev->aclk);
+	}
+}
+
+static void rk3288_lcdc_disable_irq(struct lcdc_device *lcdc_dev)
+{
+	u32 mask, val;
+
+	if (likely(lcdc_dev->clk_on)) {
+		spin_lock(&lcdc_dev->reg_lock);
+		mask = M_DSP_HOLD_VALID_INTR_EN | M_FS_INTR_EN |
+			M_LINE_FLAG_INTR_EN | M_BUS_ERROR_INTR_EN;
+		val = V_DSP_HOLD_VALID_INTR_EN(0) | V_FS_INTR_EN(0) |
+			V_LINE_FLAG_INTR_EN(0) | V_BUS_ERROR_INTR_EN(0);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL0, mask, val);
+
+		mask = M_DSP_HOLD_VALID_INTR_CLR | M_FS_INTR_CLR |
+			M_LINE_FLAG_INTR_CLR | M_LINE_FLAG_INTR_CLR;
+		val = V_DSP_HOLD_VALID_INTR_CLR(0) | V_FS_INTR_CLR(0) |
+			V_LINE_FLAG_INTR_CLR(0) | V_BUS_ERROR_INTR_CLR(0);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL0, mask, val);
+
+		mask = M_WIN0_EMPTY_INTR_EN | M_WIN1_EMPTY_INTR_EN |
+			M_WIN2_EMPTY_INTR_EN | M_WIN3_EMPTY_INTR_EN |
+			M_HWC_EMPTY_INTR_EN | M_POST_BUF_EMPTY_INTR_EN |
+			M_POST_BUF_EMPTY_INTR_EN;
+		val = V_WIN0_EMPTY_INTR_EN(0) | V_WIN1_EMPTY_INTR_EN(0) |
+			V_WIN2_EMPTY_INTR_EN(0) | V_WIN3_EMPTY_INTR_EN(0) |
+			V_HWC_EMPTY_INTR_EN(0) | V_POST_BUF_EMPTY_INTR_EN(0) |
+			V_PWM_GEN_INTR_EN(0);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL1, mask, val);
+
+		mask = M_WIN0_EMPTY_INTR_CLR | M_WIN1_EMPTY_INTR_CLR |
+			M_WIN2_EMPTY_INTR_CLR | M_WIN3_EMPTY_INTR_CLR |
+			M_HWC_EMPTY_INTR_CLR | M_POST_BUF_EMPTY_INTR_CLR |
+			M_POST_BUF_EMPTY_INTR_CLR;
+		val = V_WIN0_EMPTY_INTR_CLR(0) | V_WIN1_EMPTY_INTR_CLR(0) |
+			V_WIN2_EMPTY_INTR_CLR(0) | V_WIN3_EMPTY_INTR_CLR(0) |
+			V_HWC_EMPTY_INTR_CLR(0) |
+			V_POST_BUF_EMPTY_INTR_CLR(0) |
+			V_PWM_GEN_INTR_CLR(0);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL1, mask, val);
+		lcdc_cfg_done(lcdc_dev);
+		spin_unlock(&lcdc_dev->reg_lock);
+	}
+}
+
+static void rk3288_lcdc_enable_irq(struct lcdc_device *lcdc_dev)
+{
+	u32 mask, val;
+
+	if (likely(lcdc_dev->clk_on)) {
+		spin_lock(&lcdc_dev->reg_lock);
+		mask = M_FS_INTR_CLR | M_FS_INTR_EN;
+		val = V_FS_INTR_CLR(1) | V_FS_INTR_EN(1);
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL0, mask, val);
+		spin_unlock(&lcdc_dev->reg_lock);
+	}
+}
+
+static irqreturn_t rk3288_lcdc_isr(int irq, void *dev_id)
+{
+	struct lcdc_device *lcdc_dev =
+			(struct lcdc_device *)dev_id;
+	u32 intr0_reg;
+
+	intr0_reg = lcdc_readl(lcdc_dev, INTR_CTRL0);
+	if (intr0_reg & M_FS_INTR_STS) {
+		lcdc_msk_reg(lcdc_dev, INTR_CTRL0, M_FS_INTR_CLR,
+			     V_FS_INTR_CLR(1));
+		lcdc_vsync_event_handler(lcdc_dev->dev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void rk3288_lcdc_alpha_cfg(struct lcdc_device *lcdc_dev,
+				  struct lcdc_win_data *layer)
+{
+	struct alpha_config alpha_config;
+
+	u32 mask, val;
+	int ppixel_alpha, global_alpha;
+	u32 src_alpha_ctl, dst_alpha_ctl;
+
+	ppixel_alpha = ((layer->format == ARGB888) ||
+			(layer->format == ABGR888)) ? 1 : 0;
+	global_alpha = (layer->g_alpha_val == 0) ? 0 : 1;
+	alpha_config.src_global_alpha_val = layer->g_alpha_val;
+	layer->alpha_mode = AB_SRC_OVER;
+	switch (layer->alpha_mode) {
+	case AB_USER_DEFINE:
+		break;
+	case AB_CLEAR:
+		alpha_config.src_factor_mode = AA_ZERO;
+		alpha_config.dst_factor_mode = AA_ZERO;
+		break;
+	case AB_SRC:
+		alpha_config.src_factor_mode = AA_ONE;
+		alpha_config.dst_factor_mode = AA_ZERO;
+		break;
+	case AB_DST:
+		alpha_config.src_factor_mode = AA_ZERO;
+		alpha_config.dst_factor_mode = AA_ONE;
+		break;
+	case AB_SRC_OVER:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		if (global_alpha)
+			alpha_config.src_factor_mode = AA_SRC_GLOBAL;
+		else
+			alpha_config.src_factor_mode = AA_ONE;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	case AB_DST_OVER:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_INVERSE;
+		alpha_config.dst_factor_mode = AA_ONE;
+		break;
+	case AB_SRC_IN:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC;
+		alpha_config.dst_factor_mode = AA_ZERO;
+		break;
+	case AB_DST_IN:
+		alpha_config.src_factor_mode = AA_ZERO;
+		alpha_config.dst_factor_mode = AA_SRC;
+		break;
+	case AB_SRC_OUT:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_INVERSE;
+		alpha_config.dst_factor_mode = AA_ZERO;
+		break;
+	case AB_DST_OUT:
+		alpha_config.src_factor_mode = AA_ZERO;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	case AB_SRC_ATOP:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	case AB_DST_ATOP:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_INVERSE;
+		alpha_config.dst_factor_mode = AA_SRC;
+		break;
+	case XOR:
+		alpha_config.src_color_mode = AA_SRC_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_INVERSE;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	case AB_SRC_OVER_GLOBAL:
+		alpha_config.src_global_alpha_mode = AA_PER_PIX_GLOBAL;
+		alpha_config.src_color_mode = AA_SRC_NO_PRE_MUL;
+		alpha_config.src_factor_mode = AA_SRC_GLOBAL;
+		alpha_config.dst_factor_mode = AA_SRC_INVERSE;
+		break;
+	default:
+		pr_err("alpha mode error\n");
+		break;
+	}
+	if ((ppixel_alpha == 1) && (global_alpha == 1))
+		alpha_config.src_global_alpha_mode = AA_PER_PIX_GLOBAL;
+	else if (ppixel_alpha == 1)
+		alpha_config.src_global_alpha_mode = AA_PER_PIX;
+	else if (global_alpha == 1)
+		alpha_config.src_global_alpha_mode = AA_GLOBAL;
+	else
+		dev_warn(lcdc_dev->dev, "alpha_en should be 0\n");
+	alpha_config.src_alpha_mode = AA_STRAIGHT;
+	alpha_config.src_alpha_cal_m0 = AA_NO_SAT;
+
+	switch (layer->id) {
+	case 0:
+		src_alpha_ctl = 0x60;
+		dst_alpha_ctl = 0x64;
+		break;
+	case 1:
+		src_alpha_ctl = 0xa0;
+		dst_alpha_ctl = 0xa4;
+		break;
+	case 2:
+		src_alpha_ctl = 0xdc;
+		dst_alpha_ctl = 0xec;
+		break;
+	case 3:
+		src_alpha_ctl = 0x12c;
+		dst_alpha_ctl = 0x13c;
+		break;
+	}
+	mask = M_WIN0_DST_FACTOR_M0;
+	val = V_WIN0_DST_FACTOR_M0(alpha_config.dst_factor_mode);
+	lcdc_msk_reg(lcdc_dev, dst_alpha_ctl, mask, val);
+	mask = M_WIN0_SRC_ALPHA_EN | M_WIN0_SRC_COLOR_M0 |
+		M_WIN0_SRC_ALPHA_M0 | M_WIN0_SRC_BLEND_M0 |
+		M_WIN0_SRC_ALPHA_CAL_M0 | M_WIN0_SRC_FACTOR_M0|
+		M_WIN0_SRC_GLOBAL_ALPHA;
+	val = V_WIN0_SRC_ALPHA_EN(1) |
+		V_WIN0_SRC_COLOR_M0(alpha_config.src_color_mode) |
+		V_WIN0_SRC_ALPHA_M0(alpha_config.src_alpha_mode) |
+		V_WIN0_SRC_BLEND_M0(alpha_config.src_global_alpha_mode) |
+		V_WIN0_SRC_ALPHA_CAL_M0(alpha_config.src_alpha_cal_m0) |
+		V_WIN0_SRC_FACTOR_M0(alpha_config.src_factor_mode) |
+		V_WIN0_SRC_GLOBAL_ALPHA(alpha_config.src_global_alpha_val);
+	lcdc_msk_reg(lcdc_dev, src_alpha_ctl, mask, val);
+}
+
+static void rk3288_lcdc_win01_set(struct lcdc_device *lcdc_dev,
+				  struct lcdc_win_data *win)
+{
+	unsigned int mask, val;
+	unsigned int off = win->id * 0x40;
+	struct drm_display_mode *mode = lcdc_dev->mode;
+
+	spin_lock(&lcdc_dev->reg_lock);
+
+	if (mode && win->enabled) {
+		u32 dsp_stx = win->xpos + mode->htotal - mode->hsync_start;
+		u32 dsp_sty = win->ypos + mode->vtotal - mode->vsync_start;
+
+		mask = M_WIN0_EN | M_WIN0_DATA_FMT | M_WIN0_FMT_10 |
+			M_WIN0_LB_MODE | M_WIN0_RB_SWAP;
+		val = V_WIN0_EN(1) | V_WIN0_DATA_FMT(win->format) |
+			V_WIN0_FMT_10(win->fmt_10) |
+			V_WIN0_LB_MODE(win->win_lb_mode) |
+			V_WIN0_RB_SWAP(win->swap_rb);
+		lcdc_msk_reg(lcdc_dev, WIN0_CTRL0+off, mask, val);
+
+		mask = M_WIN0_BIC_COE_SEL |
+			M_WIN0_VSD_YRGB_GT4 | M_WIN0_VSD_YRGB_GT2 |
+			M_WIN0_VSD_CBR_GT4 | M_WIN0_VSD_CBR_GT2 |
+			M_WIN0_YRGB_HOR_SCL_MODE | M_WIN0_YRGB_VER_SCL_MODE |
+			M_WIN0_YRGB_HSD_MODE | M_WIN0_YRGB_VSU_MODE |
+			M_WIN0_YRGB_VSD_MODE | M_WIN0_CBR_HOR_SCL_MODE |
+			M_WIN0_CBR_VER_SCL_MODE | M_WIN0_CBR_HSD_MODE |
+			M_WIN0_CBR_VSU_MODE | M_WIN0_CBR_VSD_MODE;
+		val = V_WIN0_BIC_COE_SEL(win->bic_coe_el) |
+			V_WIN0_VSD_YRGB_GT4(win->vsd_yrgb_gt4) |
+			V_WIN0_VSD_YRGB_GT2(win->vsd_yrgb_gt2) |
+			V_WIN0_VSD_CBR_GT4(win->vsd_cbr_gt4) |
+			V_WIN0_VSD_CBR_GT2(win->vsd_cbr_gt2) |
+			V_WIN0_YRGB_HOR_SCL_MODE(win->yrgb_hor_scl_mode) |
+			V_WIN0_YRGB_VER_SCL_MODE(win->yrgb_ver_scl_mode) |
+			V_WIN0_YRGB_HSD_MODE(win->yrgb_hsd_mode) |
+			V_WIN0_YRGB_VSU_MODE(win->yrgb_vsu_mode) |
+			V_WIN0_YRGB_VSD_MODE(win->yrgb_vsd_mode) |
+			V_WIN0_CBR_HOR_SCL_MODE(win->cbr_hor_scl_mode) |
+			V_WIN0_CBR_VER_SCL_MODE(win->cbr_ver_scl_mode) |
+			V_WIN0_CBR_HSD_MODE(win->cbr_hsd_mode) |
+			V_WIN0_CBR_VSU_MODE(win->cbr_vsu_mode) |
+			V_WIN0_CBR_VSD_MODE(win->cbr_vsd_mode);
+		lcdc_msk_reg(lcdc_dev, WIN0_CTRL1+off, mask, val);
+
+		val = V_WIN0_VIR_STRIDE(win->y_vir_stride) |
+			V_WIN0_VIR_STRIDE_UV(win->uv_vir_stride);
+		lcdc_writel(lcdc_dev, WIN0_VIR+off, val);
+		lcdc_writel(lcdc_dev, WIN0_YRGB_MST+off, win->yrgb_addr);
+		lcdc_writel(lcdc_dev, WIN0_CBR_MST+off, win->uv_addr);
+		val = V_WIN0_ACT_WIDTH(win->xact) |
+			V_WIN0_ACT_HEIGHT(win->yact);
+		lcdc_writel(lcdc_dev, WIN0_ACT_INFO+off, val);
+
+		val = V_WIN0_DSP_WIDTH(win->xsize) |
+			V_WIN0_DSP_HEIGHT(win->ysize);
+		lcdc_writel(lcdc_dev, WIN0_DSP_INFO+off, val);
+
+		val = V_WIN0_DSP_XST(dsp_stx) |
+			V_WIN0_DSP_YST(dsp_sty);
+		lcdc_writel(lcdc_dev, WIN0_DSP_ST+off, val);
+
+		val = V_WIN0_HS_FACTOR_YRGB(0x1000) |
+			V_WIN0_VS_FACTOR_YRGB(0x1000);
+		lcdc_writel(lcdc_dev, WIN0_SCL_FACTOR_YRGB+off, val);
+
+		val = V_WIN0_HS_FACTOR_CBR(0x1000) |
+			V_WIN0_VS_FACTOR_CBR(0x1000);
+		lcdc_writel(lcdc_dev, WIN0_SCL_FACTOR_CBR+off, val);
+
+		lcdc_writel(lcdc_dev, WIN0_COLOR_KEY, 0x80000000);
+		if (win->alpha_en == 1) {
+			rk3288_lcdc_alpha_cfg(lcdc_dev, win);
+		} else {
+			mask = M_WIN0_SRC_ALPHA_EN;
+			val = V_WIN0_SRC_ALPHA_EN(0);
+			lcdc_msk_reg(lcdc_dev,
+				     WIN0_SRC_ALPHA_CTRL+off, mask, val);
+		}
+	} else {
+		mask = M_WIN0_EN;
+		val = V_WIN0_EN(0);
+		lcdc_msk_reg(lcdc_dev, WIN0_CTRL0+off, mask, val);
+	}
+
+	spin_unlock(&lcdc_dev->reg_lock);
+}
+
+static void rk3288_lcdc_win23_set(struct lcdc_device *lcdc_dev,
+				  struct lcdc_win_data *win)
+{
+	unsigned int mask, val;
+	unsigned int off = (win->id-2) * 0x50;
+	struct drm_display_mode *mode = lcdc_dev->mode;
+
+	if (mode && win->enabled) {
+		u32 dsp_stx = win->xpos + mode->htotal - mode->hdisplay;
+		u32 dsp_sty = win->ypos + mode->vtotal - mode->vdisplay;
+
+		mask = M_WIN2_EN | M_WIN2_DATA_FMT | M_WIN2_RB_SWAP;
+		val = V_WIN2_EN(1) | V_WIN2_DATA_FMT(win->fmt_cfg) |
+			V_WIN2_RB_SWAP(win->swap_rb);
+		lcdc_msk_reg(lcdc_dev, WIN2_CTRL0+off, mask, val);
+		mask = M_WIN2_MST0_EN;
+		val = V_WIN2_MST0_EN(1);
+		lcdc_msk_reg(lcdc_dev, WIN2_CTRL0+off, mask, val);
+
+		mask = M_WIN2_VIR_STRIDE0;
+		val = V_WIN2_VIR_STRIDE0(win->y_vir_stride);
+		lcdc_msk_reg(lcdc_dev, WIN2_VIR0_1+off, mask, val);
+
+		lcdc_writel(lcdc_dev, WIN2_MST0+off, win->yrgb_addr);
+		val = V_WIN2_DSP_WIDTH0(win->xsize) |
+			V_WIN2_DSP_HEIGHT0(win->ysize);
+		lcdc_writel(lcdc_dev, WIN2_DSP_INFO0+off, val);
+		val = V_WIN2_DSP_XST0(dsp_stx) |
+			V_WIN2_DSP_YST0(dsp_sty);
+		lcdc_writel(lcdc_dev, WIN2_DSP_ST0+off, val);
+		if (win->alpha_en == 1) {
+			rk3288_lcdc_alpha_cfg(lcdc_dev, win);
+		} else {
+			mask = M_WIN2_SRC_ALPHA_EN;
+			val = V_WIN2_SRC_ALPHA_EN(0);
+			lcdc_msk_reg(lcdc_dev,
+				     WIN2_SRC_ALPHA_CTRL+off, mask, val);
+		}
+	} else {
+		mask = M_WIN2_EN | M_WIN2_MST0_EN |
+			M_WIN2_MST0_EN | M_WIN2_MST2_EN |
+			M_WIN2_MST3_EN;
+		val = V_WIN2_EN(0) | V_WIN2_MST0_EN(0) |
+			V_WIN2_MST1_EN(0) | V_WIN2_MST2_EN(0) |
+			V_WIN2_MST3_EN(0);
+		lcdc_msk_reg(lcdc_dev, WIN2_CTRL0+off, mask, val);
+	}
+}
+
+static int rk3288_lcdc_initial(struct lcdc_device *lcdc_dev)
+{
+	int i;
+
+	lcdc_dev->hclk = devm_clk_get(lcdc_dev->dev, "hclk_lcdc");
+	lcdc_dev->aclk = devm_clk_get(lcdc_dev->dev, "aclk_lcdc");
+	lcdc_dev->dclk = devm_clk_get(lcdc_dev->dev, "dclk_lcdc");
+
+	if ((IS_ERR(lcdc_dev->aclk)) || (IS_ERR(lcdc_dev->dclk)) ||
+	    (IS_ERR(lcdc_dev->hclk))) {
+		dev_err(lcdc_dev->dev, "failed to get lcdc%d clk source\n",
+			lcdc_dev->id);
+		return -ENODEV;
+	}
+
+	if (rk3288_lcdc_clk_enable(lcdc_dev) < 0) {
+		dev_err(lcdc_dev->dev, "failed to enable lcdc%d clks\n",
+			lcdc_dev->id);
+		return -ENODEV;
+	}
+
+	memcpy(lcdc_dev->regsbak, lcdc_dev->regs, lcdc_dev->len);
+
+	lcdc_set_bit(lcdc_dev, SYS_CTRL, M_AUTO_GATING_EN);
+	lcdc_cfg_done(lcdc_dev);
+
+	lcdc_dev->standby = DRM_MODE_DPMS_OFF;
+
+	for (i = 0; i < ARRAY_SIZE(lcdc_win); i++) {
+		lcdc_win[i].enabled = false;
+		rk3288_lcdc_win_commit(&lcdc_dev->lcdc_drv, &lcdc_win[i]);
+	}
+
+	return 0;
+}
+
+static struct lcdc_driver *rk3288_lcdc_init(struct platform_device *pdev)
+{
+	struct lcdc_device *lcdc_dev;
+	struct lcdc_driver *lcdc_drv;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct device_node *np;
+	int ret = 0;
+
+	if (!dev->of_node)
+		return NULL;
+
+	np = pdev->dev.of_node;
+
+	/* if the primary lcdc has not registered, the extend
+	 * lcdc register later
+	*/
+	lcdc_dev = devm_kzalloc(dev, sizeof(struct lcdc_device), GFP_KERNEL);
+	if (!lcdc_dev) {
+		dev_err(&pdev->dev, "rk3288 lcdc device kmalloc fail!");
+		return NULL;
+	}
+	lcdc_dev->dev = dev;
+	lcdc_drv = &lcdc_dev->lcdc_drv;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lcdc_dev->reg_phy_base = res->start;
+	lcdc_dev->len = resource_size(res);
+	lcdc_dev->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(lcdc_dev->regs)) {
+		dev_err(&pdev->dev, "ioremap lcdc devices fail\n");
+		return NULL;
+	}
+
+	lcdc_dev->regsbak = devm_kzalloc(dev, lcdc_dev->len, GFP_KERNEL);
+	if (IS_ERR(lcdc_dev->regsbak)) {
+		dev_err(&pdev->dev, "lcdc devices kzalloc reg backup fail\n");
+		return NULL;
+	}
+
+	lcdc_dev->id = rk3288_lcdc_get_id(lcdc_dev->reg_phy_base);
+	if (lcdc_dev->id < 0) {
+		dev_err(&pdev->dev, "no such lcdc device id[%d]!\n",
+			lcdc_dev->id);
+		return NULL;
+	}
+
+	dev_set_name(lcdc_dev->dev, "lcdc%d", lcdc_dev->id);
+
+	lcdc_dev->irq = platform_get_irq(pdev, 0);
+	if (lcdc_dev->irq < 0) {
+		dev_err(&pdev->dev, "cannot find IRQ for lcdc%d\n",
+			lcdc_dev->id);
+		return NULL;
+	}
+
+	ret = devm_request_irq(dev, lcdc_dev->irq, rk3288_lcdc_isr,
+			       IRQF_DISABLED | IRQF_SHARED,
+			       dev_name(dev), lcdc_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "cannot requeset irq %d - err %d\n",
+			lcdc_dev->irq, ret);
+		return NULL;
+	}
+
+	spin_lock_init(&lcdc_dev->reg_lock);
+
+	ret = rk3288_lcdc_initial(lcdc_dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "cannot initial lcdc dev - err %d\n", ret);
+		return NULL;
+	}
+
+	dev_info(dev, "lcdc%d probe ok\n", lcdc_dev->id);
+
+	return lcdc_drv;
+}
+
+static void rk3288_lcdc_deinit(struct lcdc_driver *lcdc_drv)
+{
+}
+
+static void rk3288_lcdc_dpms(struct lcdc_driver *lcdc_drv, int mode)
+{
+	struct lcdc_device *lcdc_dev =
+			container_of(lcdc_drv, struct lcdc_device, lcdc_drv);
+
+	if (lcdc_dev->standby == mode)
+		return;
+
+	if (mode == DRM_MODE_DPMS_ON) {
+		if (rk3288_lcdc_clk_enable(lcdc_dev) < 0) {
+			dev_err(lcdc_dev->dev, "failed to enable lcdc%d clks\n",
+				lcdc_dev->id);
+			return;
+		}
+
+		spin_lock(&lcdc_dev->reg_lock);
+		lcdc_msk_reg(lcdc_dev, SYS_CTRL, M_STANDBY_EN, V_STANDBY_EN(0));
+		spin_unlock(&lcdc_dev->reg_lock);
+	} else {
+		if (lcdc_dev->clk_on) {
+			spin_lock(&lcdc_dev->reg_lock);
+			lcdc_msk_reg(lcdc_dev, SYS_CTRL,
+				     M_STANDBY_EN, V_STANDBY_EN(1));
+			spin_unlock(&lcdc_dev->reg_lock);
+			rk3288_lcdc_clk_disable(lcdc_dev);
+		}
+	}
+
+	lcdc_dev->standby = mode;
+}
+
+static void rk3288_lcdc_mode_set(struct lcdc_driver *lcdc_drv,
+				 struct drm_display_mode *mode)
+{
+	struct lcdc_device *lcdc_dev =
+			container_of(lcdc_drv, struct lcdc_device, lcdc_drv);
+	struct rockchip_panel_special *priv_mode = (void *)mode->private;
+	u32 mask, val;
+	u16 x_res = mode->hdisplay;
+	u16 y_res = mode->vdisplay;
+	u16 h_total = mode->htotal;
+	u16 v_total = mode->vtotal;
+	u16 hsync_len = mode->hsync_end - mode->hsync_start;
+	u16 left_margin = mode->htotal - mode->hsync_end;
+	u16 vsync_len = mode->vsync_end - mode->vsync_start;
+	u16 upper_margin = mode->vtotal - mode->vsync_end;
+
+	u16 face = priv_mode->out_face;
+	u8 pin_hsync = (priv_mode->flags & DISPLAY_FLAGS_HSYNC_HIGH) ? 1 : 0;
+	u8 pin_vsync = (priv_mode->flags & DISPLAY_FLAGS_VSYNC_HIGH) ? 1 : 0;
+	u8 pin_den = (priv_mode->flags & DISPLAY_FLAGS_DE_HIGH) ? 1 : 0;
+	u8 pin_dclk = (priv_mode->flags &
+		       DISPLAY_FLAGS_PIXDATA_NEGEDGE) ? 1 : 0;
+	u8 swap_rb = (priv_mode->color_swap & ROCKCHIP_COLOR_SWAP_RB) ? 1 : 0;
+	u8 swap_rg = (priv_mode->color_swap & ROCKCHIP_COLOR_SWAP_RG) ? 1 : 0;
+	u8 swap_gb = (priv_mode->color_swap & ROCKCHIP_COLOR_SWAP_GB) ? 1 : 0;
+	u8 swap_dumy = 0;
+	u8 swap_delta = 0;
+	bool dither = priv_mode->dither;
+	u8 type = priv_mode->out_type;
+
+	spin_lock(&lcdc_dev->reg_lock);
+	if (likely(lcdc_dev->clk_on)) {
+		val = 0;
+		switch (face) {
+		case ROCKCHIP_OUTFACE_P565:
+			val = V_DITHER_DOWN_EN(1) | V_DITHER_DOWN_MODE(0) |
+				V_DITHER_DOWN_SEL(1);
+			break;
+		case ROCKCHIP_OUTFACE_P666:
+			val = V_DITHER_DOWN_EN(1) | V_DITHER_DOWN_MODE(1) |
+				V_DITHER_DOWN_SEL(1);
+			break;
+		case ROCKCHIP_OUTFACE_P888:
+			break;
+		default:
+			dev_err(lcdc_dev->dev, "un supported interface[%d]!\n",
+				face);
+			break;
+		}
+
+		mask = M_DITHER_DOWN_EN | M_DITHER_DOWN_MODE |
+			M_DITHER_DOWN_SEL;
+		lcdc_msk_reg(lcdc_dev, DSP_CTRL1, mask, val);
+
+		if (dither)
+			face = ROCKCHIP_OUTFACE_P888;
+
+		switch (type) {
+		case ROCKCHIP_DISPLAY_TYPE_RGB:
+		case ROCKCHIP_DISPLAY_TYPE_LVDS:
+			mask = M_RGB_OUT_EN;
+			val = V_RGB_OUT_EN(1);
+			break;
+		case ROCKCHIP_DISPLAY_TYPE_HDMI:
+			face = ROCKCHIP_OUTFACE_AAAA;
+			mask = M_HDMI_OUT_EN;
+			val = V_HDMI_OUT_EN(1);
+			break;
+		case ROCKCHIP_DISPLAY_TYPE_MIPI:
+			mask = M_MIPI_OUT_EN;
+			val = V_MIPI_OUT_EN(1);
+			break;
+		case ROCKCHIP_DISPLAY_TYPE_EDP:
+			face = ROCKCHIP_OUTFACE_AAAA;
+			mask = M_DITHER_DOWN_EN | M_DITHER_UP_EN;
+			val = V_DITHER_DOWN_EN(0) | V_DITHER_UP_EN(0);
+			lcdc_msk_reg(lcdc_dev, DSP_CTRL1, mask, val);
+			mask = M_EDP_OUT_EN;
+			val = V_EDP_OUT_EN(1);
+			break;
+		default:
+			dev_err(lcdc_dev->dev, "unsupported display type[%d]\n",
+				type);
+		}
+		lcdc_msk_reg(lcdc_dev, SYS_CTRL, mask, val);
+
+		mask = M_DSP_OUT_MODE | M_DSP_HSYNC_POL | M_DSP_VSYNC_POL |
+			M_DSP_DEN_POL | M_DSP_DCLK_POL | M_DSP_BG_SWAP |
+			M_DSP_RB_SWAP | M_DSP_RG_SWAP | M_DSP_DELTA_SWAP |
+			M_DSP_DUMMY_SWAP | M_DSP_OUT_ZERO | M_DSP_BLANK_EN |
+			M_DSP_BLACK_EN | M_DSP_X_MIR_EN | M_DSP_Y_MIR_EN;
+		val = V_DSP_OUT_MODE(face) | V_DSP_HSYNC_POL(pin_hsync) |
+			V_DSP_VSYNC_POL(pin_vsync) |
+			V_DSP_DEN_POL(pin_den) | V_DSP_DCLK_POL(pin_dclk) |
+			V_DSP_BG_SWAP(swap_gb) | V_DSP_RB_SWAP(swap_rb) |
+			V_DSP_RG_SWAP(swap_rg) |
+			V_DSP_DELTA_SWAP(swap_delta) |
+			V_DSP_DUMMY_SWAP(swap_dumy) | V_DSP_OUT_ZERO(0) |
+			V_DSP_BLANK_EN(0) | V_DSP_BLACK_EN(0);
+		lcdc_msk_reg(lcdc_dev, DSP_CTRL0, mask, val);
+
+		mask = M_DSP_BG_BLUE | M_DSP_BG_GREEN | M_DSP_BG_RED;
+		val = V_DSP_BG_BLUE(0) | V_DSP_BG_GREEN(0) | V_DSP_BG_RED(0);
+		lcdc_msk_reg(lcdc_dev, DSP_BG, mask, val);
+
+		mask = M_DSP_HS_PW | M_DSP_HTOTAL;
+		val = V_DSP_HS_PW(hsync_len) | V_DSP_HTOTAL(h_total);
+		lcdc_msk_reg(lcdc_dev, DSP_HTOTAL_HS_END, mask, val);
+
+		mask = M_DSP_HACT_END | M_DSP_HACT_ST;
+		val = V_DSP_HACT_END(hsync_len + left_margin + x_res) |
+			V_DSP_HACT_ST(hsync_len + left_margin);
+		lcdc_msk_reg(lcdc_dev, DSP_HACT_ST_END, mask, val);
+
+		mask = M_DSP_VS_PW | M_DSP_VTOTAL;
+		val = V_DSP_VS_PW(vsync_len) | V_DSP_VTOTAL(v_total);
+		lcdc_msk_reg(lcdc_dev, DSP_VTOTAL_VS_END, mask, val);
+
+		mask = M_DSP_VACT_END | M_DSP_VACT_ST;
+		val = V_DSP_VACT_END(vsync_len + upper_margin + y_res) |
+			V_DSP_VACT_ST(vsync_len + upper_margin);
+		lcdc_msk_reg(lcdc_dev, DSP_VACT_ST_END, mask, val);
+
+		mask = M_DSP_HACT_END_POST | M_DSP_HACT_ST_POST;
+		val = V_DSP_HACT_END_POST(hsync_len + left_margin + x_res) |
+			V_DSP_HACT_ST_POST(hsync_len + left_margin);
+		lcdc_msk_reg(lcdc_dev, POST_DSP_HACT_INFO, mask, val);
+
+		mask = M_DSP_VACT_END_POST | M_DSP_VACT_ST_POST;
+		val = V_DSP_VACT_END_POST(vsync_len + upper_margin + y_res) |
+			V_DSP_VACT_ST_POST(vsync_len + upper_margin);
+		lcdc_msk_reg(lcdc_dev, POST_DSP_VACT_INFO, mask, val);
+	}
+
+	spin_unlock(&lcdc_dev->reg_lock);
+	clk_set_rate(lcdc_dev->dclk, mode->clock * 1000);
+
+	lcdc_dev->mode = mode;
+}
+
+static void rk3288_lcdc_enable_vblank(struct lcdc_driver *lcdc_drv)
+{
+	rk3288_lcdc_enable_irq(container_of(lcdc_drv,
+					    struct lcdc_device, lcdc_drv));
+}
+
+static void rk3288_lcdc_disable_vblank(struct lcdc_driver *lcdc_drv)
+{
+	rk3288_lcdc_disable_irq(container_of(lcdc_drv,
+					     struct lcdc_device, lcdc_drv));
+}
+
+static struct lcdc_win_data *
+	rk3288_lcdc_get_win(struct lcdc_driver *lcdc_drv, int zpos)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(lcdc_win); i++) {
+		if (lcdc_win[i].zpos == zpos)
+			return &lcdc_win[i];
+	}
+
+	return NULL;
+}
+
+static void rk3288_lcdc_win_commit(struct lcdc_driver *lcdc_drv,
+				   struct lcdc_win_data *win)
+{
+	struct lcdc_device *lcdc_dev =
+			container_of(lcdc_drv, struct lcdc_device, lcdc_drv);
+	struct device *dev = lcdc_dev->dev;
+
+	switch (win->id) {
+	case 0:
+	case 1:
+		rk3288_lcdc_win01_set(lcdc_dev, win);
+		break;
+	case 2:
+	case 3:
+		rk3288_lcdc_win23_set(lcdc_dev, win);
+		break;
+	default:
+		dev_info(dev, "not support win%d\n", win->id);
+	}
+
+	spin_lock(&lcdc_dev->reg_lock);
+	lcdc_cfg_done(lcdc_dev);
+	spin_unlock(&lcdc_dev->reg_lock);
+}
+
+struct lcdc_driver_data rockchip_rk3288_lcdc = {
+	.init = rk3288_lcdc_init,
+	.deinit = rk3288_lcdc_deinit,
+	.dpms = rk3288_lcdc_dpms,
+	.mode_set = rk3288_lcdc_mode_set,
+	.enable_vblank = rk3288_lcdc_enable_vblank,
+	.disable_vblank = rk3288_lcdc_disable_vblank,
+	.get_win = rk3288_lcdc_get_win,
+	.win_commit = rk3288_lcdc_win_commit,
+	.num_win = ARRAY_SIZE(lcdc_win),
+};
diff --git a/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h b/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h
new file mode 100644
index 0000000..d2e1110
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/lcdc/rk3288_lcdc.h
@@ -0,0 +1,1202 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *      mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * 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 _RK3288_LCDC_H_
+#define _RK3288_LCDC_H_
+
+#define SET_BIT(x, bit) ((x) << (bit))
+#define SET_BIT_MASK(x, bit, mask) SET_BIT((x) & (mask), bit)
+
+#define GPIO		0
+#define REGULATOR	1
+
+/* register definition */
+#define REG_CFG_DONE			(0x0000)
+#define VERSION_INFO			(0x0004)
+#define M_RTL_VERSION			SET_BIT(0xffff, 0)
+#define M_FPGA_VERSION			SET_BIT(0xffff, 16)
+#define SYS_CTRL			(0x0008)
+#define V_DIRECT_PATH_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_DIRECT_PATCH_SEL(x)		SET_BIT_MASK(x, 1, 3)
+#define V_DOUB_CHANNEL_EN(x)		SET_BIT_MASK(x, 3, 1)
+#define V_DOUB_CH_OVERLAP_NUM(x)	SET_BIT_MASK(x, 4, 0xf)
+#define V_EDPI_HALT_EN(x)		SET_BIT_MASK(x, 8, 1)
+#define V_EDPI_WMS_MODE(x)		SET_BIT_MASK(x, 9, 1)
+#define V_EDPI_WMS_FS(x)		SET_BIT_MASK(x, 10, 1)
+#define V_RGB_OUT_EN(x)			SET_BIT_MASK(x, 12, 1)
+#define V_HDMI_OUT_EN(x)		SET_BIT_MASK(x, 13, 1)
+#define V_EDP_OUT_EN(x)			SET_BIT_MASK(x, 14, 1)
+#define V_MIPI_OUT_EN(x)		SET_BIT_MASK(x, 15, 1)
+#define V_DMA_BURST_LENGTH(x)		SET_BIT_MASK(x, 18, 3)
+#define V_MMU_EN(x)			SET_BIT_MASK(x, 20, 1)
+#define V_DMA_STOP(x)			SET_BIT_MASK(x, 21, 1)
+#define V_STANDBY_EN(x)			SET_BIT_MASK(x, 22, 1)
+#define V_AUTO_GATING_EN(x)		SET_BIT_MASK(x, 23, 1)
+
+#define M_DIRECT_PATH_EN		SET_BIT(1, 0)
+#define M_DIRECT_PATCH_SEL		SET_BIT(3, 1)
+#define M_DOUB_CHANNEL_EN		SET_BIT(1, 3)
+#define M_DOUB_CH_OVERLAP_NUM		SET_BIT(0xf, 4)
+#define M_EDPI_HALT_EN			SET_BIT(1, 8)
+#define M_EDPI_WMS_MODE			SET_BIT(1, 9)
+#define M_EDPI_WMS_FS			SET_BIT(1, 10)
+#define M_RGB_OUT_EN			SET_BIT(1, 12)
+#define M_HDMI_OUT_EN			SET_BIT(1, 13)
+#define M_EDP_OUT_EN			SET_BIT(1, 14)
+#define M_MIPI_OUT_EN			SET_BIT(1, 15)
+#define M_DMA_BURST_LENGTH		SET_BIT(3, 18)
+#define M_MMU_EN			SET_BIT(1, 20)
+#define M_DMA_STOP			SET_BIT(1, 21)
+#define M_STANDBY_EN			SET_BIT(1, 22)
+#define M_AUTO_GATING_EN		SET_BIT(1, 23)
+#define SYS_CTRL1			(0x000c)
+#define V_NOC_HURRY_EN(x)		SET_BIT_MASK(x, 0, 0x1)
+#define V_NOC_HURRY_VALUE(x)		SET_BIT_MASK(x, 1, 0x3)
+#define V_NOC_HURRY_THRESHOLD(x)	SET_BIT_MASK(x, 3, 0x3f)
+#define V_NOC_QOS_EN(x)			SET_BIT_MASK(x, 9, 0x1)
+#define V_NOC_WIN_QOS(x)		SET_BIT_MASK(x, 10, 0x3)
+#define V_AXI_MAX_OUTSTANDING_EN(x)	SET_BIT_MASK(x, 12, 0x1)
+#define V_AXI_OUTSTANDING_MAX_NUM(x)	SET_BIT_MASK(x, 13, 0x1f)
+
+#define M_NOC_HURRY_EN			SET_BIT(0x1, 0)
+#define M_NOC_HURRY_VALUE		SET_BIT(0x3, 1)
+#define M_NOC_HURRY_THRESHOLD		SET_BIT(0x3f, 3)
+#define M_NOC_QOS_EN			SET_BIT(0x1, 9)
+#define M_NOC_WIN_QOS			SET_BIT(0x3, 10)
+#define M_AXI_MAX_OUTSTANDING_EN	SET_BIT(0x1, 12)
+#define M_AXI_OUTSTANDING_MAX_NUM	SET_BIT(0x1f, 13)
+
+#define DSP_CTRL0		(0x0010)
+#define V_DSP_OUT_MODE(x)	SET_BIT_MASK(x, 0, 0x0f)
+#define V_DSP_HSYNC_POL(x)	SET_BIT_MASK(x, 4, 1)
+#define V_DSP_VSYNC_POL(x)	SET_BIT_MASK(x, 5, 1)
+#define V_DSP_DEN_POL(x)	SET_BIT_MASK(x, 6, 1)
+#define V_DSP_DCLK_POL(x)	SET_BIT_MASK(x, 7, 1)
+#define V_DSP_DCLK_DDR(x)	SET_BIT_MASK(x, 8, 1)
+#define V_DSP_DDR_PHASE(x)	SET_BIT_MASK(x, 9, 1)
+#define V_DSP_INTERLACE(x)	SET_BIT_MASK(x, 10, 1)
+#define V_DSP_FIELD_POL(x)	SET_BIT_MASK(x, 11, 1)
+#define V_DSP_BG_SWAP(x)	SET_BIT_MASK(x, 12, 1)
+#define V_DSP_RB_SWAP(x)	SET_BIT_MASK(x, 13, 1)
+#define V_DSP_RG_SWAP(x)	SET_BIT_MASK(x, 14, 1)
+#define V_DSP_DELTA_SWAP(x)	SET_BIT_MASK(x, 15, 1)
+#define V_DSP_DUMMY_SWAP(x)	SET_BIT_MASK(x, 16, 1)
+#define V_DSP_OUT_ZERO(x)	SET_BIT_MASK(x, 17, 1)
+#define V_DSP_BLANK_EN(x)	SET_BIT_MASK(x, 18, 1)
+#define V_DSP_BLACK_EN(x)	SET_BIT_MASK(x, 19, 1)
+#define V_DSP_CCIR656_AVG(x)	SET_BIT_MASK(x, 20, 1)
+#define V_DSP_YUV_CLIP(x)	SET_BIT_MASK(x, 21, 1)
+#define V_DSP_X_MIR_EN(x)	SET_BIT_MASK(x, 22, 1)
+#define V_DSP_Y_MIR_EN(x)	SET_BIT_MASK(x, 23, 1)
+#define M_DSP_OUT_MODE		SET_BIT(0x0f, 0)
+#define M_DSP_HSYNC_POL		SET_BIT(1, 4)
+#define M_DSP_VSYNC_POL		SET_BIT(1, 5)
+#define M_DSP_DEN_POL		SET_BIT(1, 6)
+#define M_DSP_DCLK_POL		SET_BIT(1, 7)
+#define M_DSP_DCLK_DDR		SET_BIT(1, 8)
+#define M_DSP_DDR_PHASE		SET_BIT(1, 9)
+#define M_DSP_INTERLACE		SET_BIT(1, 10)
+#define M_DSP_FIELD_POL		SET_BIT(1, 11)
+#define M_DSP_BG_SWAP		SET_BIT(1, 12)
+#define M_DSP_RB_SWAP		SET_BIT(1, 13)
+#define M_DSP_RG_SWAP		SET_BIT(1, 14)
+#define M_DSP_DELTA_SWAP	SET_BIT(1, 15)
+#define M_DSP_DUMMY_SWAP	SET_BIT(1, 16)
+#define M_DSP_OUT_ZERO		SET_BIT(1, 17)
+#define M_DSP_BLANK_EN		SET_BIT(1, 18)
+#define M_DSP_BLACK_EN		SET_BIT(1, 19)
+#define M_DSP_CCIR656_AVG	SET_BIT(1, 20)
+#define M_DSP_YUV_CLIP		SET_BIT(1, 21)
+#define M_DSP_X_MIR_EN		SET_BIT(1, 22)
+#define M_DSP_Y_MIR_EN		SET_BIT(1, 23)
+
+#define DSP_CTRL1		(0x0014)
+#define V_DSP_LUT_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_PRE_DITHER_DOWN_EN(x)	SET_BIT_MASK(x, 1, 1)
+#define V_DITHER_DOWN_EN(x)	SET_BIT_MASK(x, 2, 1)
+#define V_DITHER_DOWN_MODE(x)	SET_BIT_MASK(x, 3, 1)
+#define V_DITHER_DOWN_SEL(x)	SET_BIT_MASK(x, 4, 1)
+#define V_DITHER_UP_EN(x)	SET_BIT_MASK(x, 6, 1)
+#define V_DSP_LAYER0_SEL(x)	SET_BIT_MASK(x, 8, 3)
+#define V_DSP_LAYER1_SEL(x)	SET_BIT_MASK(x, 10, 3)
+#define V_DSP_LAYER2_SEL(x)	SET_BIT_MASK(x, 12, 3)
+#define V_DSP_LAYER3_SEL(x)	SET_BIT_MASK(x, 14, 3)
+#define M_DSP_LUT_EN		SET_BIT(1, 0)
+#define M_PRE_DITHER_DOWN_EN	SET_BIT(1, 1)
+#define M_DITHER_DOWN_EN	SET_BIT(1, 2)
+#define M_DITHER_DOWN_MODE	SET_BIT(1, 3)
+#define M_DITHER_DOWN_SEL	SET_BIT(1, 4)
+#define M_DITHER_UP_EN		SET_BIT(1, 6)
+#define M_DSP_LAYER0_SEL	SET_BIT(3, 8)
+#define M_DSP_LAYER1_SEL	SET_BIT(3, 10)
+#define M_DSP_LAYER2_SEL	SET_BIT(3, 12)
+#define M_DSP_LAYER3_SEL	SET_BIT(3, 14)
+
+#define DSP_BG			(0x0018)
+#define V_DSP_BG_BLUE(x)	SET_BIT_MASK(x << 2, 0, 0x3ff)
+#define V_DSP_BG_GREEN(x)	SET_BIT_MASK(x << 2, 10, 0x3ff)
+#define V_DSP_BG_RED(x)		SET_BIT_MASK(x << 2, 20, 0x3ff)
+#define M_DSP_BG_BLUE		SET_BIT(0x3ff, 0)
+#define M_DSP_BG_GREEN		SET_BIT(0x3ff, 10)
+#define M_DSP_BG_RED		SET_BIT(0x3ff, 20)
+
+#define MCU_CTRL		(0x001c)
+#define V_MCU_PIX_TOTAL(x)	SET_BIT_MASK(x, 0, 0x3f)
+#define V_MCU_CS_PST(x)		SET_BIT_MASK(x, 6, 0xf)
+#define V_MCU_CS_PEND(x)	SET_BIT_MASK(x, 10, 0x3f)
+#define V_MCU_RW_PST(x)		SET_BIT_MASK(x, 16, 0xf)
+#define V_MCU_RW_PEND(x)	SET_BIT_MASK(x, 20, 0x3f)
+#define V_MCU_CLK_SEL(x)	SET_BIT_MASK(x, 26, 1)
+#define V_MCU_HOLD_MODE(x)	SET_BIT_MASK(x, 27, 1)
+#define V_MCU_FRAME_ST(x)	SET_BIT_MASK(x, 28, 1)
+#define V_MCU_RS(x)		SET_BIT_MASK(x, 29, 1)
+#define V_MCU_BYPASS(x)		SET_BIT_MASK(x, 30, 1)
+#define V_MCU_TYPE(x)		SET_BIT_MASK(x, 31, 1)
+#define M_MCU_PIX_TOTAL		SET_BIT(0x3f, 0)
+#define M_MCU_CS_PST		SET_BIT(0xf, 6)
+#define M_MCU_CS_PEND		SET_BIT(0x3f, 10)
+#define M_MCU_RW_PST		SET_BIT(0xf, 16)
+#define M_MCU_RW_PEND		SET_BIT(0x3f, 20)
+#define M_MCU_CLK_SEL		SET_BIT(1, 26)
+#define M_MCU_HOLD_MODE		SET_BIT(1, 27)
+#define M_MCU_FRAME_ST		SET_BIT(1, 28)
+#define M_MCU_RS		SET_BIT(1, 29)
+#define M_MCU_BYPASS		SET_BIT(1, 30)
+#define M_MCU_TYPE		SET_BIT((u32)1, 31)
+
+#define INTR_CTRL0			(0x0020)
+#define V_DSP_HOLD_VALID_INTR_STS(x)	SET_BIT_MASK(x, 0, 1)
+#define V_FS_INTR_STS(x)		SET_BIT_MASK(x, 1, 1)
+#define V_LINE_FLAG_INTR_STS(x)		SET_BIT_MASK(x, 2, 1)
+#define V_BUS_ERROR_INTR_STS(x)		SET_BIT_MASK(x, 3, 1)
+#define V_DSP_HOLD_VALID_INTR_EN(x)	SET_BIT_MASK(x, 4, 1)
+#define V_FS_INTR_EN(x)			SET_BIT_MASK(x, 5, 1)
+#define V_LINE_FLAG_INTR_EN(x)		SET_BIT_MASK(x, 6, 1)
+#define V_BUS_ERROR_INTR_EN(x)		SET_BIT_MASK(x, 7, 1)
+#define V_DSP_HOLD_VALID_INTR_CLR(x)	SET_BIT_MASK(x, 8, 1)
+#define V_FS_INTR_CLR(x)		SET_BIT_MASK(x, 9, 1)
+#define V_LINE_FLAG_INTR_CLR(x)		SET_BIT_MASK(x, 10, 1)
+#define V_BUS_ERROR_INTR_CLR(x)		SET_BIT_MASK(x, 11, 1)
+#define V_DSP_LINE_FLAG_NUM(x)		SET_BIT_MASK(x, 12, 0xfff)
+
+#define M_DSP_HOLD_VALID_INTR_STS	SET_BIT(1, 0)
+#define M_FS_INTR_STS			SET_BIT(1, 1)
+#define M_LINE_FLAG_INTR_STS		SET_BIT(1, 2)
+#define M_BUS_ERROR_INTR_STS		SET_BIT(1, 3)
+#define M_DSP_HOLD_VALID_INTR_EN	SET_BIT(1, 4)
+#define M_FS_INTR_EN			SET_BIT(1, 5)
+#define M_LINE_FLAG_INTR_EN		SET_BIT(1, 6)
+#define M_BUS_ERROR_INTR_EN		SET_BIT(1, 7)
+#define M_DSP_HOLD_VALID_INTR_CLR	SET_BIT(1, 8)
+#define M_FS_INTR_CLR			SET_BIT(1, 9)
+#define M_LINE_FLAG_INTR_CLR		SET_BIT(1, 10)
+#define M_BUS_ERROR_INTR_CLR		SET_BIT(1, 11)
+#define M_DSP_LINE_FLAG_NUM		SET_BIT(0xfff, 12)
+
+#define INTR_CTRL1			(0x0024)
+#define V_WIN0_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 0, 1)
+#define V_WIN1_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 1, 1)
+#define V_WIN2_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 2, 1)
+#define V_WIN3_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 3, 1)
+#define V_HWC_EMPTY_INTR_STS(x)		SET_BIT_MASK(x, 4, 1)
+#define V_POST_BUF_EMPTY_INTR_STS(x)	SET_BIT_MASK(x, 5, 1)
+#define V_PWM_GEN_INTR_STS(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN0_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 8, 1)
+#define V_WIN1_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 9, 1)
+#define V_WIN2_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 10, 1)
+#define V_WIN3_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 11, 1)
+#define V_HWC_EMPTY_INTR_EN(x)		SET_BIT_MASK(x, 12, 1)
+#define V_POST_BUF_EMPTY_INTR_EN(x)	SET_BIT_MASK(x, 13, 1)
+#define V_PWM_GEN_INTR_EN(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN0_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 16, 1)
+#define V_WIN1_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 17, 1)
+#define V_WIN2_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 18, 1)
+#define V_WIN3_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 19, 1)
+#define V_HWC_EMPTY_INTR_CLR(x)		SET_BIT_MASK(x, 20, 1)
+#define V_POST_BUF_EMPTY_INTR_CLR(x)	SET_BIT_MASK(x, 21, 1)
+#define V_PWM_GEN_INTR_CLR(x)		SET_BIT_MASK(x, 22, 1)
+
+#define M_WIN0_EMPTY_INTR_STS		SET_BIT(1, 0)
+#define M_WIN1_EMPTY_INTR_STS		SET_BIT(1, 1)
+#define M_WIN2_EMPTY_INTR_STS		SET_BIT(1, 2)
+#define M_WIN3_EMPTY_INTR_STS		SET_BIT(1, 3)
+#define M_HWC_EMPTY_INTR_STS		SET_BIT(1, 4)
+#define M_POST_BUF_EMPTY_INTR_STS	SET_BIT(1, 5)
+#define M_PWM_GEN_INTR_STS		SET_BIT(1, 6)
+#define M_WIN0_EMPTY_INTR_EN		SET_BIT(1, 8)
+#define M_WIN1_EMPTY_INTR_EN		SET_BIT(1, 9)
+#define M_WIN2_EMPTY_INTR_EN		SET_BIT(1, 10)
+#define M_WIN3_EMPTY_INTR_EN		SET_BIT(1, 11)
+#define M_HWC_EMPTY_INTR_EN		SET_BIT(1, 12)
+#define M_POST_BUF_EMPTY_INTR_EN	SET_BIT(1, 13)
+#define M_PWM_GEN_INTR_EN		SET_BIT(1, 14)
+#define M_WIN0_EMPTY_INTR_CLR		SET_BIT(1, 16)
+#define M_WIN1_EMPTY_INTR_CLR		SET_BIT(1, 17)
+#define M_WIN2_EMPTY_INTR_CLR		SET_BIT(1, 18)
+#define M_WIN3_EMPTY_INTR_CLR		SET_BIT(1, 19)
+#define M_HWC_EMPTY_INTR_CLR		SET_BIT(1, 20)
+#define M_POST_BUF_EMPTY_INTR_CLR	SET_BIT(1, 21)
+#define M_PWM_GEN_INTR_CLR		SET_BIT(1, 22)
+
+/* win0 register */
+#define WIN0_CTRL0			(0x0030)
+#define V_WIN0_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_WIN0_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_WIN0_FMT_10(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN0_LB_MODE(x)		SET_BIT_MASK(x, 5, 7)
+#define V_WIN0_INTERLACE_READ(x)	SET_BIT_MASK(x, 8, 1)
+#define V_WIN0_NO_OUTSTANDING(x)	SET_BIT_MASK(x, 9, 1)
+#define V_WIN0_CSC_MODE(x)		SET_BIT_MASK(x, 10, 3)
+#define V_WIN0_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_WIN0_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_WIN0_MID_SWAP(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN0_UV_SWAP(x)		SET_BIT_MASK(x, 15, 1)
+#define V_WIN0_PPAS_ZERO_EN(x)		SET_BIT_MASK(x, 16, 1)
+#define V_WIN0_YRGB_DEFLICK(x)		SET_BIT_MASK(x, 18, 1)
+#define V_WIN0_CBR_DEFLICK(x)		SET_BIT_MASK(x, 19, 1)
+#define V_WIN0_YUV_CLIP(x)		SET_BIT_MASK(x, 20, 1)
+
+#define M_WIN0_EN			SET_BIT(1, 0)
+#define M_WIN0_DATA_FMT			SET_BIT(7, 1)
+#define M_WIN0_FMT_10			SET_BIT(1, 4)
+#define M_WIN0_LB_MODE			SET_BIT(7, 5)
+#define M_WIN0_INTERLACE_READ		SET_BIT(1, 8)
+#define M_WIN0_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_WIN0_CSC_MODE			SET_BIT(3, 10)
+#define M_WIN0_RB_SWAP			SET_BIT(1, 12)
+#define M_WIN0_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_WIN0_MID_SWAP			SET_BIT(1, 14)
+#define M_WIN0_UV_SWAP			SET_BIT(1, 15)
+#define M_WIN0_PPAS_ZERO_EN		SET_BIT(1, 16)
+#define M_WIN0_YRGB_DEFLICK		SET_BIT(1, 18)
+#define M_WIN0_CBR_DEFLICK		SET_BIT(1, 19)
+#define M_WIN0_YUV_CLIP			SET_BIT(1, 20)
+
+#define WIN0_CTRL1			(0x0034)
+#define V_WIN0_YRGB_AXI_GATHER_EN(x)	SET_BIT_MASK(x, 0, 1)
+#define V_WIN0_CBR_AXI_GATHER_EN(x)	SET_BIT_MASK(x, 1, 1)
+#define V_WIN0_BIC_COE_SEL(x)		SET_BIT_MASK(x, 2, 3)
+#define V_WIN0_VSD_YRGB_GT4(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN0_VSD_YRGB_GT2(x)		SET_BIT_MASK(x, 5, 1)
+#define V_WIN0_VSD_CBR_GT4(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN0_VSD_CBR_GT2(x)		SET_BIT_MASK(x, 7, 1)
+#define V_WIN0_YRGB_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 8, 0xf)
+#define V_WIN0_CBR_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 12, 7)
+#define V_WIN0_LINE_LOAD_MODE(x)	SET_BIT_MASK(x, 15, 1)
+#define V_WIN0_YRGB_HOR_SCL_MODE(x)	SET_BIT_MASK(x, 16, 3)
+#define V_WIN0_YRGB_VER_SCL_MODE(x)	SET_BIT_MASK(x, 18, 3)
+#define V_WIN0_YRGB_HSD_MODE(x)		SET_BIT_MASK(x, 20, 3)
+#define V_WIN0_YRGB_VSU_MODE(x)		SET_BIT_MASK(x, 22, 1)
+#define V_WIN0_YRGB_VSD_MODE(x)		SET_BIT_MASK(x, 23, 1)
+#define V_WIN0_CBR_HOR_SCL_MODE(x)	SET_BIT_MASK(x, 24, 3)
+#define V_WIN0_CBR_VER_SCL_MODE(x)	SET_BIT_MASK(x, 26, 3)
+#define V_WIN0_CBR_HSD_MODE(x)		SET_BIT_MASK(x, 28, 3)
+#define V_WIN0_CBR_VSU_MODE(x)		SET_BIT_MASK(x, 30, 1)
+#define V_WIN0_CBR_VSD_MODE(x)		SET_BIT_MASK(x, 31, 1)
+
+#define M_WIN0_YRGB_AXI_GATHER_EN	SET_BIT(1, 0)
+#define M_WIN0_CBR_AXI_GATHER_EN	SET_BIT(1, 1)
+#define M_WIN0_BIC_COE_SEL		SET_BIT(3, 2)
+#define M_WIN0_VSD_YRGB_GT4		SET_BIT(1, 4)
+#define M_WIN0_VSD_YRGB_GT2		SET_BIT(1, 5)
+#define M_WIN0_VSD_CBR_GT4		SET_BIT(1, 6)
+#define M_WIN0_VSD_CBR_GT2		SET_BIT(1, 7)
+#define M_WIN0_YRGB_AXI_GATHER_NUM	SET_BIT(0xf, 8)
+#define M_WIN0_CBR_AXI_GATHER_NUM	SET_BIT(7, 12)
+#define M_WIN0_LINE_LOAD_MODE		SET_BIT(1, 15)
+#define M_WIN0_YRGB_HOR_SCL_MODE	SET_BIT(3, 16)
+#define M_WIN0_YRGB_VER_SCL_MODE	SET_BIT(3, 18)
+#define M_WIN0_YRGB_HSD_MODE		SET_BIT(3, 20)
+#define M_WIN0_YRGB_VSU_MODE		SET_BIT(1, 22)
+#define M_WIN0_YRGB_VSD_MODE		SET_BIT(1, 23)
+#define M_WIN0_CBR_HOR_SCL_MODE		SET_BIT(3, 24)
+#define M_WIN0_CBR_VER_SCL_MODE		SET_BIT(3, 26)
+#define M_WIN0_CBR_HSD_MODE		SET_BIT(3, 28)
+#define M_WIN0_CBR_VSU_MODE		SET_BIT((u32)1, 30)
+#define M_WIN0_CBR_VSD_MODE		SET_BIT((u32)1, 31)
+
+#define WIN0_COLOR_KEY			(0x0038)
+#define V_WIN0_COLOR_KEY(x)		SET_BIT_MASK(x, 0, 0x3fffffff)
+#define V_WIN0_COLOR_KEY_EN(x)		SET_BIT_MASK(x, 31, 1)
+#define M_WIN0_COLOR_KEY		SET_BIT(0x3fffffff, 0)
+#define M_WIN0_COLOR_KEY_EN		SET_BIT((u32)1, 31)
+
+#define WIN0_VIR			(0x003c)
+#define V_WIN0_VIR_STRIDE(x)		SET_BIT_MASK(x, 0, 0x3fff)
+#define V_WIN0_VIR_STRIDE_UV(x)		SET_BIT_MASK(x, 16, 0x3fff)
+#define M_WIN0_VIR_STRIDE		SET_BIT(0x3fff, 0)
+#define M_WIN0_VIR_STRIDE_UV		SET_BIT(0x3fff, 16)
+
+#define WIN0_YRGB_MST			(0x0040)
+#define WIN0_CBR_MST			(0x0044)
+#define WIN0_ACT_INFO			(0x0048)
+#define V_WIN0_ACT_WIDTH(x)		SET_BIT_MASK(x-1, 0, 0x1fff)
+#define V_WIN0_ACT_HEIGHT(x)		SET_BIT_MASK(x-1, 16, 0x1fff)
+#define M_WIN0_ACT_WIDTH		SET_BIT(0x1fff, 0)
+#define M_WIN0_ACT_HEIGHT		SET_BIT(0x1fff, 16)
+
+#define WIN0_DSP_INFO			(0x004c)
+#define V_WIN0_DSP_WIDTH(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN0_DSP_HEIGHT(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN0_DSP_WIDTH		SET_BIT(0xfff, 0)
+#define M_WIN0_DSP_HEIGHT		SET_BIT(0xfff, 16)
+
+#define WIN0_DSP_ST			(0x0050)
+#define V_WIN0_DSP_XST(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN0_DSP_YST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN0_DSP_XST			SET_BIT(0x1fff, 0)
+#define M_WIN0_DSP_YST			SET_BIT(0x1fff, 16)
+
+#define WIN0_SCL_FACTOR_YRGB		(0x0054)
+#define V_WIN0_HS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 0, 0xffff)
+#define V_WIN0_VS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 16, 0xffff)
+#define M_WIN0_HS_FACTOR_YRGB		SET_BIT(0xffff, 0)
+#define M_WIN0_VS_FACTOR_YRGB		SET_BIT((u32)0xffff, 16)
+
+#define WIN0_SCL_FACTOR_CBR		(0x0058)
+#define V_WIN0_HS_FACTOR_CBR(x)		SET_BIT_MASK(x, 0, 0xffff)
+#define V_WIN0_VS_FACTOR_CBR(x)		SET_BIT_MASK(x, 16, 0xffff)
+#define M_WIN0_HS_FACTOR_CBR		SET_BIT(0xffff, 0)
+#define M_WIN0_VS_FACTOR_CBR		SET_BIT((u32)0xffff, 16)
+
+#define WIN0_SCL_OFFSET			(0x005c)
+#define V_WIN0_HS_OFFSET_YRGB(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN0_HS_OFFSET_CBR(x)		SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN0_VS_OFFSET_YRGB(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN0_VS_OFFSET_CBR(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN0_HS_OFFSET_YRGB		SET_BIT(0xff, 0)
+#define M_WIN0_HS_OFFSET_CBR		SET_BIT(0xff, 8)
+#define M_WIN0_VS_OFFSET_YRGB		SET_BIT(0xff, 16)
+#define M_WIN0_VS_OFFSET_CBR		SET_BIT((u32)0xff, 24)
+
+#define WIN0_SRC_ALPHA_CTRL		(0x0060)
+#define V_WIN0_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN0_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_WIN0_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_WIN0_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_WIN0_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_WIN0_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_WIN0_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN0_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN0_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_WIN0_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_WIN0_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_WIN0_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_WIN0_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_WIN0_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_WIN0_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_WIN0_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define WIN0_DST_ALPHA_CTRL		(0x0064)
+#define V_WIN0_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_WIN0_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define WIN0_FADING_CTRL		(0x0068)
+#define V_WIN0_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN0_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN0_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN0_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_WIN0_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_WIN0_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_WIN0_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_WIN0_FADING_EN		SET_BIT(1, 24)
+
+/* win1 register */
+#define WIN1_CTRL0			(0x0070)
+#define V_WIN1_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_WIN1_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_WIN1_FMT_10(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN1_LB_MODE(x)		SET_BIT_MASK(x, 5, 7)
+#define V_WIN1_INTERLACE_READ_MODE(x)	SET_BIT_MASK(x, 8, 1)
+#define V_WIN1_NO_OUTSTANDING(x)	SET_BIT_MASK(x, 9, 1)
+#define V_WIN1_CSC_MODE(x)		SET_BIT_MASK(x, 10, 3)
+#define V_WIN1_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_WIN1_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_WIN1_MID_SWAP(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN1_UV_SWAP(x)		SET_BIT_MASK(x, 15, 1)
+#define V_WIN1_PPAS_ZERO_EN(x)		SET_BIT_MASK(x, 16, 1)
+#define V_WIN1_YRGB_DEFLICK(x)		SET_BIT_MASK(x, 18, 1)
+#define V_WIN1_CBR_DEFLICK(x)		SET_BIT_MASK(x, 19, 1)
+#define V_WIN1_YUV_CLIP(x)		SET_BIT_MASK(x, 20, 1)
+
+#define M_WIN1_EN			SET_BIT(1, 0)
+#define M_WIN1_DATA_FMT			SET_BIT(7, 1)
+#define M_WIN1_FMT_10			SET_BIT(1, 4)
+#define M_WIN1_LB_MODE			SET_BIT(7, 5)
+#define M_WIN1_INTERLACE_READ_MODE	SET_BIT(1, 8)
+#define M_WIN1_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_WIN1_CSC_MODE			SET_BIT(3, 10)
+#define M_WIN1_RB_SWAP			SET_BIT(1, 12)
+#define M_WIN1_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_WIN1_MID_SWAP			SET_BIT(1, 14)
+#define M_WIN1_UV_SWAP			SET_BIT(1, 15)
+#define M_WIN1_PPAS_ZERO_EN		SET_BIT(1, 16)
+#define M_WIN1_YRGB_DEFLICK		SET_BIT(1, 18)
+#define M_WIN1_CBR_DEFLICK		SET_BIT(1, 19)
+#define M_WIN1_YUV_CLIP			SET_BIT(1, 20)
+
+#define WIN1_CTRL1			(0x0074)
+#define V_WIN1_YRGB_AXI_GATHER_EN(x)	SET_BIT_MASK(x, 0, 1)
+#define V_WIN1_CBR_AXI_GATHER_EN(x)	SET_BIT_MASK(x, 1, 1)
+#define V_WIN1_BIC_COE_SEL(x)		SET_BIT_MASK(x, 2, 3)
+#define V_WIN1_VSD_YRGB_GT4(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN1_VSD_YRGB_GT2(x)		SET_BIT_MASK(x, 5, 1)
+#define V_WIN1_VSD_CBR_GT4(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN1_VSD_CBR_GT2(x)		SET_BIT_MASK(x, 7, 1)
+#define V_WIN1_YRGB_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 8, 0xf)
+#define V_WIN1_CBR_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 12, 7)
+#define V_WIN1_LINE_LOAD_MODE(x)	SET_BIT_MASK(x, 15, 1)
+#define V_WIN1_YRGB_HOR_SCL_MODE(x)	SET_BIT_MASK(x, 16, 3)
+#define V_WIN1_YRGB_VER_SCL_MODE(x)	SET_BIT_MASK(x, 18, 3)
+#define V_WIN1_YRGB_HSD_MODE(x)		SET_BIT_MASK(x, 20, 3)
+#define V_WIN1_YRGB_VSU_MODE(x)		SET_BIT_MASK(x, 22, 1)
+#define V_WIN1_YRGB_VSD_MODE(x)		SET_BIT_MASK(x, 23, 1)
+#define V_WIN1_CBR_HOR_SCL_MODE(x)	SET_BIT_MASK(x, 24, 3)
+#define V_WIN1_CBR_VER_SCL_MODE(x)	SET_BIT_MASK(x, 26, 3)
+#define V_WIN1_CBR_HSD_MODE(x)		SET_BIT_MASK(x, 28, 3)
+#define V_WIN1_CBR_VSU_MODE(x)		SET_BIT_MASK(x, 30, 1)
+#define V_WIN1_CBR_VSD_MODE(x)		SET_BIT_MASK(x, 31, 1)
+
+#define M_WIN1_YRGB_AXI_GATHER_EN	SET_BIT(1, 0)
+#define M_WIN1_CBR_AXI_GATHER_EN	SET_BIT(1, 1)
+#define M_WIN1_BIC_COE_SEL		SET_BIT(3, 2)
+#define M_WIN1_VSD_YRGB_GT4		SET_BIT(1, 4)
+#define M_WIN1_VSD_YRGB_GT2		SET_BIT(1, 5)
+#define M_WIN1_VSD_CBR_GT4		SET_BIT(1, 6)
+#define M_WIN1_VSD_CBR_GT2		SET_BIT(1, 7)
+#define M_WIN1_YRGB_AXI_GATHER_NUM	SET_BIT(0xf, 8)
+#define M_WIN1_CBR_AXI_GATHER_NUM	SET_BIT(7, 12)
+#define M_WIN1_LINE_LOAD_MODE		SET_BIT(1, 15)
+#define M_WIN1_YRGB_HOR_SCL_MODE	SET_BIT(3, 16)
+#define M_WIN1_YRGB_VER_SCL_MODE	SET_BIT(3, 18)
+#define M_WIN1_YRGB_HSD_MODE		SET_BIT(3, 20)
+#define M_WIN1_YRGB_VSU_MODE		SET_BIT(1, 22)
+#define M_WIN1_YRGB_VSD_MODE		SET_BIT(1, 23)
+#define M_WIN1_CBR_HOR_SCL_MODE		SET_BIT(3, 24)
+#define M_WIN1_CBR_VER_SCL_MODE		SET_BIT(3, 26)
+#define M_WIN1_CBR_HSD_MODE		SET_BIT(3, 28)
+#define M_WIN1_CBR_VSU_MODE		SET_BIT(1, 30)
+#define M_WIN1_CBR_VSD_MODE		SET_BIT((u32)1, 31)
+
+#define WIN1_COLOR_KEY			(0x0078)
+#define V_WIN1_COLOR_KEY(x)		SET_BIT_MASK(x, 0, 0x3fffffff)
+#define V_WIN1_COLOR_KEY_EN(x)		SET_BIT_MASK(x, 31, 1)
+#define M_WIN1_COLOR_KEY		SET_BIT(0x3fffffff, 0)
+#define M_WIN1_COLOR_KEY_EN		SET_BIT((u32)1, 31)
+
+#define WIN1_VIR			(0x007c)
+#define V_WIN1_VIR_STRIDE(x)		SET_BIT_MASK(x, 0, 0x3fff)
+#define V_WIN1_VIR_STRIDE_UV(x)		SET_BIT_MASK(x, 16, 0x3fff)
+#define M_WIN1_VIR_STRIDE		SET_BIT(0x3fff, 0)
+#define M_WIN1_VIR_STRIDE_UV		SET_BIT(0x3fff, 16)
+
+#define WIN1_YRGB_MST			(0x0080)
+#define WIN1_CBR_MST			(0x0084)
+#define WIN1_ACT_INFO			(0x0088)
+#define V_WIN1_ACT_WIDTH(x)		SET_BIT_MASK(x-1, 0, 0x1fff)
+#define V_WIN1_ACT_HEIGHT(x)		SET_BIT_MASK(x-1, 16, 0x1fff)
+#define M_WIN1_ACT_WIDTH		SET_BIT(0x1fff, 0)
+#define M_WIN1_ACT_HEIGHT		SET_BIT(0x1fff, 16)
+
+#define WIN1_DSP_INFO			(0x008c)
+#define V_WIN1_DSP_WIDTH(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN1_DSP_HEIGHT(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN1_DSP_WIDTH		SET_BIT(0xfff, 0)
+#define M_WIN1_DSP_HEIGHT		SET_BIT(0xfff, 16)
+
+#define WIN1_DSP_ST			(0x0090)
+#define V_WIN1_DSP_XST(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN1_DSP_YST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN1_DSP_XST			SET_BIT(0x1fff, 0)
+#define M_WIN1_DSP_YST			SET_BIT(0x1fff, 16)
+
+#define WIN1_SCL_FACTOR_YRGB		(0x0094)
+#define V_WIN1_HS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 0, 0xffff)
+#define V_WIN1_VS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 16, 0xffff)
+#define M_WIN1_HS_FACTOR_YRGB		SET_BIT(0xffff, 0)
+#define M_WIN1_VS_FACTOR_YRGB		SET_BIT((u32)0xffff, 16)
+
+#define WIN1_SCL_FACTOR_CBR		(0x0098)
+#define V_WIN1_HS_FACTOR_CBR(x)		SET_BIT_MASK(x, 0, 0xffff)
+#define V_WIN1_VS_FACTOR_CBR(x)		SET_BIT_MASK(x, 16, 0xffff)
+#define M_WIN1_HS_FACTOR_CBR		SET_BIT(0xffff, 0)
+#define M_WIN1_VS_FACTOR_CBR		SET_BIT((u32)0xffff, 16)
+
+#define WIN1_SCL_OFFSET			(0x009c)
+#define V_WIN1_HS_OFFSET_YRGB(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN1_HS_OFFSET_CBR(x)		SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN1_VS_OFFSET_YRGB(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN1_VS_OFFSET_CBR(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN1_HS_OFFSET_YRGB		SET_BIT(0xff, 0)
+#define M_WIN1_HS_OFFSET_CBR		SET_BIT(0xff, 8)
+#define M_WIN1_VS_OFFSET_YRGB		SET_BIT(0xff, 16)
+#define M_WIN1_VS_OFFSET_CBR		SET_BIT((u32)0xff, 24)
+
+#define WIN1_SRC_ALPHA_CTRL		(0x00a0)
+#define V_WIN1_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN1_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_WIN1_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_WIN1_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_WIN1_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_WIN1_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_WIN1_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN1_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN1_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_WIN1_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_WIN1_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_WIN1_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_WIN1_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_WIN1_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_WIN1_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_WIN1_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define WIN1_DST_ALPHA_CTRL		(0x00a4)
+#define V_WIN1_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_WIN1_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define WIN1_FADING_CTRL		(0x00a8)
+#define V_WIN1_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN1_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN1_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN1_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_WIN1_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_WIN1_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_WIN1_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_WIN1_FADING_EN		SET_BIT(1, 24)
+
+/* win2 register */
+#define WIN2_CTRL0			(0x00b0)
+#define V_WIN2_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_WIN2_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_WIN2_MST0_EN(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN2_MST1_EN(x)		SET_BIT_MASK(x, 5, 1)
+#define V_WIN2_MST2_EN(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN2_MST3_EN(x)		SET_BIT_MASK(x, 7, 1)
+#define V_WIN2_INTERLACE_READ(x)	SET_BIT_MASK(x, 8, 1)
+#define V_WIN2_NO_OUTSTANDING(x)	SET_BIT_MASK(x, 9, 1)
+#define V_WIN2_CSC_MODE(x)		SET_BIT_MASK(x, 10, 1)
+#define V_WIN2_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_WIN2_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_WIN2_ENDIAN_MODE(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN2_LUT_EN(x)		SET_BIT_MASK(x, 18, 1)
+
+#define M_WIN2_EN			SET_BIT(1, 0)
+#define M_WIN2_DATA_FMT			SET_BIT(7, 1)
+#define M_WIN2_MST0_EN			SET_BIT(1, 4)
+#define M_WIN2_MST1_EN			SET_BIT(1, 5)
+#define M_WIN2_MST2_EN			SET_BIT(1, 6)
+#define M_WIN2_MST3_EN			SET_BIT(1, 7)
+#define M_WIN2_INTERLACE_READ		SET_BIT(1, 8)
+#define M_WIN2_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_WIN2_CSC_MODE			SET_BIT(1, 10)
+#define M_WIN2_RB_SWAP			SET_BIT(1, 12)
+#define M_WIN2_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_WIN2_ENDIAN_MODE		SET_BIT(1, 14)
+#define M_WIN2_LUT_EN			SET_BIT(1, 18)
+
+#define WIN2_CTRL1			(0x00b4)
+#define V_WIN2_AXI_GATHER_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN2_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 4, 0xf)
+#define M_WIN2_AXI_GATHER_EN		SET_BIT(1, 0)
+#define M_WIN2_AXI_GATHER_NUM		SET_BIT(0xf, 4)
+
+#define WIN2_VIR0_1			(0x00b8)
+#define V_WIN2_VIR_STRIDE0(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_VIR_STRIDE1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_VIR_STRIDE0		SET_BIT(0x1fff, 0)
+#define M_WIN2_VIR_STRIDE1		SET_BIT(0x1fff, 16)
+
+#define WIN2_VIR2_3			(0x00bc)
+#define V_WIN2_VIR_STRIDE2(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_VIR_STRIDE3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_VIR_STRIDE2		SET_BIT(0x1fff, 0)
+#define M_WIN2_VIR_STRIDE3		SET_BIT(0x1fff, 16)
+
+#define WIN2_MST0			(0x00c0)
+#define WIN2_DSP_INFO0			(0x00c4)
+#define V_WIN2_DSP_WIDTH0(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN2_DSP_HEIGHT0(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN2_DSP_WIDTH0		SET_BIT(0xfff, 0)
+#define M_WIN2_DSP_HEIGHT0		SET_BIT(0xfff, 16)
+
+#define WIN2_DSP_ST0			(0x00c8)
+#define V_WIN2_DSP_XST0(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_DSP_YST0(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_DSP_XST0			SET_BIT(0x1fff, 0)
+#define M_WIN2_DSP_YST0			SET_BIT(0x1fff, 16)
+
+#define WIN2_COLOR_KEY			(0x00cc)
+#define V_WIN2_COLOR_KEY(x)		SET_BIT_MASK(x, 0, 0xffffff)
+#define V_WIN2_KEY_EN(x)		SET_BIT_MASK(x, 24, 1)
+#define M_WIN2_COLOR_KEY		SET_BIT(0xffffff, 0)
+#define M_WIN2_KEY_EN			SET_BIT((u32)1, 24)
+
+#define WIN2_MST1			(0x00d0)
+#define WIN2_DSP_INFO1			(0x00d4)
+#define V_WIN2_DSP_WIDTH1(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN2_DSP_HEIGHT1(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+
+#define M_WIN2_DSP_WIDTH1		SET_BIT(0xfff, 0)
+#define M_WIN2_DSP_HEIGHT1		SET_BIT(0xfff, 16)
+
+#define WIN2_DSP_ST1			(0x00d8)
+#define V_WIN2_DSP_XST1(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_DSP_YST1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+
+#define M_WIN2_DSP_XST1			SET_BIT(0x1fff, 0)
+#define M_WIN2_DSP_YST1			SET_BIT(0x1fff, 16)
+
+#define WIN2_SRC_ALPHA_CTRL		(0x00dc)
+#define V_WIN2_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN2_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_WIN2_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_WIN2_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_WIN2_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_WIN2_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_WIN2_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN2_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN2_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_WIN2_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_WIN2_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_WIN2_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_WIN2_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_WIN2_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_WIN2_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_WIN2_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define WIN2_MST2			(0x00e0)
+#define WIN2_DSP_INFO2			(0x00e4)
+#define V_WIN2_DSP_WIDTH2(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN2_DSP_HEIGHT2(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+
+#define M_WIN2_DSP_WIDTH2		SET_BIT(0xfff, 0)
+#define M_WIN2_DSP_HEIGHT2		SET_BIT(0xfff, 16)
+
+#define WIN2_DSP_ST2			(0x00e8)
+#define V_WIN2_DSP_XST2(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_DSP_YST2(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_DSP_XST2			SET_BIT(0x1fff, 0)
+#define M_WIN2_DSP_YST2			SET_BIT(0x1fff, 16)
+
+#define WIN2_DST_ALPHA_CTRL		(0x00ec)
+#define V_WIN2_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_WIN2_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define WIN2_MST3			(0x00f0)
+#define WIN2_DSP_INFO3			(0x00f4)
+#define V_WIN2_DSP_WIDTH3(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN2_DSP_HEIGHT3(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN2_DSP_WIDTH3		SET_BIT(0xfff, 0)
+#define M_WIN2_DSP_HEIGHT3		SET_BIT(0xfff, 16)
+
+#define WIN2_DSP_ST3			(0x00f8)
+#define V_WIN2_DSP_XST3(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN2_DSP_YST3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN2_DSP_XST3			SET_BIT(0x1fff, 0)
+#define M_WIN2_DSP_YST3			SET_BIT(0x1fff, 16)
+
+#define WIN2_FADING_CTRL		(0x00fc)
+#define V_WIN2_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN2_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN2_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN2_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_WIN2_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_WIN2_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_WIN2_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_WIN2_FADING_EN		SET_BIT(1, 24)
+
+/* win3 register */
+#define WIN3_CTRL0			(0x0100)
+#define V_WIN3_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_WIN3_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_WIN3_MST0_EN(x)		SET_BIT_MASK(x, 4, 1)
+#define V_WIN3_MST1_EN(x)		SET_BIT_MASK(x, 5, 1)
+#define V_WIN3_MST2_EN(x)		SET_BIT_MASK(x, 6, 1)
+#define V_WIN3_MST3_EN(x)		SET_BIT_MASK(x, 7, 1)
+#define V_WIN3_INTERLACE_READ(x)	SET_BIT_MASK(x, 8, 1)
+#define V_WIN3_NO_OUTSTANDING(x)	SET_BIT_MASK(x, 9, 1)
+#define V_WIN3_CSC_MODE(x)		SET_BIT_MASK(x, 10, 1)
+#define V_WIN3_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_WIN3_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_WIN3_ENDIAN_MODE(x)		SET_BIT_MASK(x, 14, 1)
+#define V_WIN3_LUT_EN(x)		SET_BIT_MASK(x, 18, 1)
+
+#define M_WIN3_EN			SET_BIT(1, 0)
+#define M_WIN3_DATA_FMT			SET_BIT(7, 1)
+#define M_WIN3_MST0_EN			SET_BIT(1, 4)
+#define M_WIN3_MST1_EN			SET_BIT(1, 5)
+#define M_WIN3_MST2_EN			SET_BIT(1, 6)
+#define M_WIN3_MST3_EN			SET_BIT(1, 7)
+#define M_WIN3_INTERLACE_READ		SET_BIT(1, 8)
+#define M_WIN3_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_WIN3_CSC_MODE			SET_BIT(1, 10)
+#define M_WIN3_RB_SWAP			SET_BIT(1, 12)
+#define M_WIN3_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_WIN3_ENDIAN_MODE		SET_BIT(1, 14)
+#define M_WIN3_LUT_EN			SET_BIT(1, 18)
+
+#define WIN3_CTRL1			(0x0104)
+#define V_WIN3_AXI_GATHER_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN3_AXI_GATHER_NUM(x)	SET_BIT_MASK(x, 4, 0xf)
+#define M_WIN3_AXI_GATHER_EN		SET_BIT(1, 0)
+#define M_WIN3_AXI_GATHER_NUM		SET_BIT(0xf, 4)
+
+#define WIN3_VIR0_1			(0x0108)
+#define V_WIN3_VIR_STRIDE0(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_VIR_STRIDE1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_VIR_STRIDE0		SET_BIT(0x1fff, 0)
+#define M_WIN3_VIR_STRIDE1		SET_BIT(0x1fff, 16)
+
+#define WIN3_VIR2_3			(0x010c)
+#define V_WIN3_VIR_STRIDE2(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_VIR_STRIDE3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_VIR_STRIDE2		SET_BIT(0x1fff, 0)
+#define M_WIN3_VIR_STRIDE3		SET_BIT(0x1fff, 16)
+
+#define WIN3_MST0			(0x0110)
+#define WIN3_DSP_INFO0			(0x0114)
+#define V_WIN3_DSP_WIDTH0(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN3_DSP_HEIGHT0(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN3_DSP_WIDTH0		SET_BIT(0xfff, 0)
+#define M_WIN3_DSP_HEIGHT0		SET_BIT(0xfff, 16)
+
+#define WIN3_DSP_ST0			(0x0118)
+#define V_WIN3_DSP_XST0(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_DSP_YST0(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_DSP_XST0			SET_BIT(0x1fff, 0)
+#define M_WIN3_DSP_YST0			SET_BIT(0x1fff, 16)
+
+#define WIN3_COLOR_KEY			(0x011c)
+#define V_WIN3_COLOR_KEY(x)		SET_BIT_MASK(x, 0, 0xffffff)
+#define V_WIN3_KEY_EN(x)		SET_BIT_MASK(x, 24, 1)
+#define M_WIN3_COLOR_KEY		SET_BIT(0xffffff, 0)
+#define M_WIN3_KEY_EN			SET_BIT((u32)1, 24)
+
+#define WIN3_MST1			(0x0120)
+#define WIN3_DSP_INFO1			(0x0124)
+#define V_WIN3_DSP_WIDTH1(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN3_DSP_HEIGHT1(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN3_DSP_WIDTH1		SET_BIT(0xfff, 0)
+#define M_WIN3_DSP_HEIGHT1		SET_BIT(0xfff, 16)
+
+#define WIN3_DSP_ST1			(0x0128)
+#define V_WIN3_DSP_XST1(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_DSP_YST1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_DSP_XST1			SET_BIT(0x1fff, 0)
+#define M_WIN3_DSP_YST1			SET_BIT(0x1fff, 16)
+
+#define WIN3_SRC_ALPHA_CTRL		(0x012c)
+#define V_WIN3_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_WIN3_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_WIN3_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_WIN3_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_WIN3_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_WIN3_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_WIN3_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN3_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_WIN3_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_WIN3_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_WIN3_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_WIN3_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_WIN3_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_WIN3_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_WIN3_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_WIN3_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define WIN3_MST2			(0x0130)
+#define WIN3_DSP_INFO2			(0x0134)
+#define V_WIN3_DSP_WIDTH2(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN3_DSP_HEIGHT2(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN3_DSP_WIDTH2		SET_BIT(0xfff, 0)
+#define M_WIN3_DSP_HEIGHT2		SET_BIT(0xfff, 16)
+
+#define WIN3_DSP_ST2			(0x0138)
+#define V_WIN3_DSP_XST2(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_DSP_YST2(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_DSP_XST2			SET_BIT(0x1fff, 0)
+#define M_WIN3_DSP_YST2			SET_BIT(0x1fff, 16)
+
+#define WIN3_DST_ALPHA_CTRL		(0x013c)
+#define V_WIN3_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_WIN3_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define WIN3_MST3			(0x0140)
+#define WIN3_DSP_INFO3			(0x0144)
+#define V_WIN3_DSP_WIDTH3(x)		SET_BIT_MASK(x-1, 0, 0xfff)
+#define V_WIN3_DSP_HEIGHT3(x)		SET_BIT_MASK(x-1, 16, 0xfff)
+#define M_WIN3_DSP_WIDTH3		SET_BIT(0xfff, 0)
+#define M_WIN3_DSP_HEIGHT3		SET_BIT(0xfff, 16)
+
+#define WIN3_DSP_ST3			(0x0148)
+#define V_WIN3_DSP_XST3(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_WIN3_DSP_YST3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_WIN3_DSP_XST3			SET_BIT(0x1fff, 0)
+#define M_WIN3_DSP_YST3			SET_BIT(0x1fff, 16)
+
+#define WIN3_FADING_CTRL		(0x014c)
+#define V_WIN3_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_WIN3_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_WIN3_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_WIN3_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_WIN3_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_WIN3_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_WIN3_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_WIN3_FADING_EN		SET_BIT(1, 24)
+
+/* hwc register */
+#define HWC_CTRL0			(0x0150)
+#define V_HWC_EN(x)			SET_BIT_MASK(x, 0, 1)
+#define V_HWC_DATA_FMT(x)		SET_BIT_MASK(x, 1, 7)
+#define V_HWC_MODE(x)			SET_BIT_MASK(x, 4, 1)
+#define V_HWC_SIZE(x)			SET_BIT_MASK(x, 5, 3)
+#define V_HWC_INTERLACE_READ(x)		SET_BIT_MASK(x, 8, 1)
+#define V_HWC_NO_OUTSTANDING(x)		SET_BIT_MASK(x, 9, 1)
+#define V_HWC_CSC_MODE(x)		SET_BIT_MASK(x, 10, 1)
+#define V_HWC_RB_SWAP(x)		SET_BIT_MASK(x, 12, 1)
+#define V_HWC_ALPHA_SWAP(x)		SET_BIT_MASK(x, 13, 1)
+#define V_HWC_ENDIAN_MODE(x)		SET_BIT_MASK(x, 14, 1)
+#define V_HWC_LUT_EN(x)			SET_BIT_MASK(x, 18, 1)
+
+#define M_HWC_EN			SET_BIT(1, 0)
+#define M_HWC_DATA_FMT			SET_BIT(7, 1)
+#define M_HWC_MODE			SET_BIT(1, 4)
+#define M_HWC_SIZE			SET_BIT(3, 5)
+#define M_HWC_INTERLACE_READ		SET_BIT(1, 8)
+#define M_HWC_NO_OUTSTANDING		SET_BIT(1, 9)
+#define M_HWC_CSC_MODE			SET_BIT(1, 10)
+#define M_HWC_RB_SWAP			SET_BIT(1, 12)
+#define M_HWC_ALPHA_SWAP		SET_BIT(1, 13)
+#define M_HWC_ENDIAN_MODE		SET_BIT(1, 14)
+#define M_HWC_LUT_EN			SET_BIT(1, 18)
+
+#define HWC_CTRL1			(0x0154)
+#define V_HWC_AXI_GATHER_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_HWC_AXI_GATHER_NUM(x)		SET_BIT_MASK(x, 4, 7)
+#define M_HWC_AXI_GATHER_EN		SET_BIT(1, 0)
+#define M_HWC_AXI_GATHER_NUM		SET_BIT(7, 4)
+
+#define HWC_MST				(0x0158)
+#define HWC_DSP_ST			(0x015c)
+#define V_HWC_DSP_XST3(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_HWC_DSP_YST3(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_HWC_DSP_XST3			SET_BIT(0x1fff, 0)
+#define M_HWC_DSP_YST3			SET_BIT(0x1fff, 16)
+
+#define HWC_SRC_ALPHA_CTRL		(0x0160)
+#define V_HWC_SRC_ALPHA_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_HWC_SRC_COLOR_M0(x)		SET_BIT_MASK(x, 1, 1)
+#define V_HWC_SRC_ALPHA_M0(x)		SET_BIT_MASK(x, 2, 1)
+#define V_HWC_SRC_BLEND_M0(x)		SET_BIT_MASK(x, 3, 3)
+#define V_HWC_SRC_ALPHA_CAL_M0(x)	SET_BIT_MASK(x, 5, 1)
+#define V_HWC_SRC_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define V_HWC_SRC_GLOBAL_ALPHA(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_HWC_FADING_VALUE(x)		SET_BIT_MASK(x, 24, 0xff)
+
+#define M_HWC_SRC_ALPHA_EN		SET_BIT(1, 0)
+#define M_HWC_SRC_COLOR_M0		SET_BIT(1, 1)
+#define M_HWC_SRC_ALPHA_M0		SET_BIT(1, 2)
+#define M_HWC_SRC_BLEND_M0		SET_BIT(3, 3)
+#define M_HWC_SRC_ALPHA_CAL_M0		SET_BIT(1, 5)
+#define M_HWC_SRC_FACTOR_M0		SET_BIT(7, 6)
+#define M_HWC_SRC_GLOBAL_ALPHA		SET_BIT(0xff, 16)
+#define M_HWC_FADING_VALUE		SET_BIT(0xff, 24)
+
+#define HWC_DST_ALPHA_CTRL		(0x0164)
+#define V_HWC_DST_FACTOR_M0(x)		SET_BIT_MASK(x, 6, 7)
+#define M_HWC_DST_FACTOR_M0		SET_BIT(7, 6)
+
+#define HWC_FADING_CTRL			(0x0168)
+#define V_HWC_FADING_OFFSET_R(x)	SET_BIT_MASK(x, 0, 0xff)
+#define V_HWC_FADING_OFFSET_G(x)	SET_BIT_MASK(x, 8, 0xff)
+#define V_HWC_FADING_OFFSET_B(x)	SET_BIT_MASK(x, 16, 0xff)
+#define V_HWC_FADING_EN(x)		SET_BIT_MASK(x, 24, 1)
+
+#define M_HWC_FADING_OFFSET_R		SET_BIT(0xff, 0)
+#define M_HWC_FADING_OFFSET_G		SET_BIT(0xff, 8)
+#define M_HWC_FADING_OFFSET_B		SET_BIT(0xff, 16)
+#define M_HWC_FADING_EN			SET_BIT(1, 24)
+
+/* post process register */
+#define POST_DSP_HACT_INFO		(0x0170)
+#define V_DSP_HACT_END_POST(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_HACT_ST_POST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_HACT_END_POST		SET_BIT(0x1fff, 0)
+#define M_DSP_HACT_ST_POST		SET_BIT(0x1fff, 16)
+
+#define POST_DSP_VACT_INFO		(0x0174)
+#define V_DSP_VACT_END_POST(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VACT_ST_POST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VACT_END_POST		SET_BIT(0x1fff, 0)
+#define M_DSP_VACT_ST_POST		SET_BIT(0x1fff, 16)
+
+#define POST_SCL_FACTOR_YRGB		(0x0178)
+#define V_POST_HS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 0, 0xffff)
+#define V_POST_VS_FACTOR_YRGB(x)	SET_BIT_MASK(x, 16, 0xffff)
+#define M_POST_HS_FACTOR_YRGB		SET_BIT(0xffff, 0)
+#define M_POST_VS_FACTOR_YRGB		SET_BIT(0xffff, 16)
+
+#define POST_SCL_CTRL			(0x0180)
+#define V_POST_HOR_SD_EN(x)		SET_BIT_MASK(x, 0, 1)
+#define V_POST_VER_SD_EN(x)		SET_BIT_MASK(x, 1, 1)
+
+#define M_POST_HOR_SD_EN		SET_BIT(0x1, 0)
+#define M_POST_VER_SD_EN		SET_BIT(0x1, 1)
+
+#define POST_DSP_VACT_INFO_F1		(0x0184)
+#define V_DSP_VACT_END_POST_F1(x)	SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VACT_ST_POST_F1(x)	SET_BIT_MASK(x, 16, 0x1fff)
+
+#define M_DSP_VACT_END_POST_F1		SET_BIT(0x1fff, 0)
+#define M_DSP_VACT_ST_POST_F1		SET_BIT(0x1fff, 16)
+
+#define DSP_HTOTAL_HS_END		(0x0188)
+#define V_DSP_HS_PW(x)			SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_HTOTAL(x)			SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_HS_PW			SET_BIT(0x1fff, 0)
+#define M_DSP_HTOTAL			SET_BIT(0x1fff, 16)
+
+#define DSP_HACT_ST_END			(0x018c)
+#define V_DSP_HACT_END(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_HACT_ST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_HACT_END			SET_BIT(0x1fff, 0)
+#define M_DSP_HACT_ST			SET_BIT(0x1fff, 16)
+
+#define DSP_VTOTAL_VS_END		(0x0190)
+#define V_DSP_VS_PW(x)			SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VTOTAL(x)			SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VS_PW			SET_BIT(0x1fff, 0)
+#define M_DSP_VTOTAL			SET_BIT(0x1fff, 16)
+
+#define DSP_VACT_ST_END			(0x0194)
+#define V_DSP_VACT_END(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VACT_ST(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VACT_END			SET_BIT(0x1fff, 0)
+#define M_DSP_VACT_ST			SET_BIT(0x1fff, 16)
+
+#define DSP_VS_ST_END_F1		(0x0198)
+#define V_DSP_VS_END_F1(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VS_ST_F1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VS_END_F1			SET_BIT(0x1fff, 0)
+#define M_DSP_VS_ST_F1			SET_BIT(0x1fff, 16)
+
+#define DSP_VACT_ST_END_F1		(0x019c)
+#define V_DSP_VACT_END_F1(x)		SET_BIT_MASK(x, 0, 0x1fff)
+#define V_DSP_VAC_ST_F1(x)		SET_BIT_MASK(x, 16, 0x1fff)
+#define M_DSP_VACT_END_F1		SET_BIT(0x1fff, 0)
+#define M_DSP_VAC_ST_F1			SET_BIT(0x1fff, 16)
+
+enum lb_mode {
+	LB_YUV_3840X5 = 0x0,
+	LB_YUV_2560X8,
+	LB_RGB_3840X2,
+	LB_RGB_2560X4,
+	LB_RGB_1920X5,
+	LB_RGB_1280X8
+};
+
+enum sacle_up_mode {
+	SCALE_UP_BIL = 0x0,
+	SCALE_UP_BIC
+};
+
+enum scale_down_mode {
+	SCALE_DOWN_BIL = 0x0,
+	SCALE_DOWN_AVG
+};
+
+/* ALPHA BLENDING MODE */
+enum alpha_mode {
+	AB_USER_DEFINE = 0x0,
+	AB_CLEAR,
+	AB_SRC,
+	AB_DST,
+	AB_SRC_OVER,
+	AB_DST_OVER,
+	AB_SRC_IN,
+	AB_DST_IN,
+	AB_SRC_OUT,
+	AB_DST_OUT,
+	AB_SRC_ATOP,
+	AB_DST_ATOP,
+	XOR,
+	AB_SRC_OVER_GLOBAL
+};
+
+enum src_alpha_mode {
+	AA_STRAIGHT = 0x0,
+	AA_INVERSE
+};
+
+enum global_alpha_mode {
+	AA_GLOBAL = 0x0,
+	AA_PER_PIX,
+	AA_PER_PIX_GLOBAL
+};
+
+enum src_alpha_sel {
+	AA_SAT = 0x0,
+	AA_NO_SAT
+};
+
+enum src_color_mode {
+	AA_SRC_PRE_MUL = 0x0,
+	AA_SRC_NO_PRE_MUL
+};
+
+enum factor_mode {
+	AA_ZERO = 0x0,
+	AA_ONE,
+	AA_SRC,
+	AA_SRC_INVERSE,
+	AA_SRC_GLOBAL
+};
+
+struct lcdc_device {
+	int id;
+	struct device *dev;
+	struct lcdc_driver lcdc_drv;
+
+	struct drm_display_mode *mode;
+
+	void __iomem *regs;
+	/* back up reg */
+	void *regsbak;
+	/* physical basic address of lcdc register*/
+	u32 reg_phy_base;
+	/* physical map length of lcdc register*/
+	u32 len;
+	/* one time only one process allowed to config the register */
+	spinlock_t reg_lock;
+
+	int __iomem *dsp_lut_addr_base;
+
+	/* used for primary or extended display device */
+	int prop;
+	bool pre_init;
+	/* if aclk or hclk is closed , cess to register is not allowed */
+	bool clk_on;
+	/* active layer counter, hen atv_layer_cnt = 0, isable lcdc */
+	u8 atv_layer_cnt;
+
+	unsigned int irq;
+
+	/* lcdc AHP clk */
+	struct clk *hclk;
+	/* lcdc dclk */
+	struct clk *dclk;
+	/* lcdc share memory frequency */
+	struct clk *aclk;
+	u32 pixclock;
+	/* 1:standby, 0:wrok */
+	u32 standby;
+};
+
+struct alpha_config {
+	/* win0_src_alpha_m0 */
+	enum src_alpha_mode src_alpha_mode;
+	/* win0_src_global_alpha */
+	u32 src_global_alpha_val;
+	/* win0_src_blend_m0 */
+	enum global_alpha_mode src_global_alpha_mode;
+	/* win0_src_alpha_cal_m0 */
+	enum src_alpha_sel src_alpha_cal_m0;
+	/* win0_src_color_m0 */
+	enum src_color_mode src_color_mode;
+	/* win0_src_factor_m0 */
+	enum factor_mode src_factor_mode;
+	/* win0_dst_factor_m0 */
+	enum factor_mode dst_factor_mode;
+};
+
+static inline void lcdc_writel(struct lcdc_device *lcdc_dev, u32 offset, u32 v)
+{
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	*_pv = v;
+	writel_relaxed(v, lcdc_dev->regs + offset);
+}
+
+static inline u32 lcdc_readl(struct lcdc_device *lcdc_dev, u32 offset)
+{
+	u32 v;
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	v = readl_relaxed(lcdc_dev->regs + offset);
+	*_pv = v;
+
+	return v;
+}
+
+static inline u32 lcdc_read_bit(struct lcdc_device *lcdc_dev,
+				u32 offset, u32 msk)
+{
+	u32 _v = readl_relaxed(lcdc_dev->regs + offset);
+
+	_v &= msk;
+
+	return _v >> msk;
+}
+
+static inline void lcdc_set_bit(struct lcdc_device *lcdc_dev,
+				u32 offset, u32 msk)
+{
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	(*_pv) |= msk;
+	writel_relaxed(*_pv, lcdc_dev->regs + offset);
+}
+
+static inline void lcdc_clr_bit(struct lcdc_device *lcdc_dev,
+				u32 offset, u32 msk)
+{
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	(*_pv) &= (~msk);
+	writel_relaxed(*_pv, lcdc_dev->regs + offset);
+}
+
+static inline void lcdc_msk_reg(struct lcdc_device *lcdc_dev,
+				u32 offset, u32 msk, u32 v)
+{
+	u32 *_pv = (u32 *)lcdc_dev->regsbak;
+
+	_pv += (offset >> 2);
+	(*_pv) &= (~msk);
+	(*_pv) |= v;
+	writel_relaxed(*_pv, lcdc_dev->regs + offset);
+}
+
+static inline void lcdc_cfg_done(struct lcdc_device *lcdc_dev)
+{
+	writel_relaxed(0x01, lcdc_dev->regs + REG_CFG_DONE);
+	dsb();
+}
+#endif /* _RK3288_LCDC_H_ */
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 6/9] Add devicetree bindings for Rockchip Soc LVDS
From: mark yao @ 2014-08-04  4:53 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao
In-Reply-To: <1407127274-1356-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 .../devicetree/bindings/video/rockchip-panel.txt   |   30 ++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/Documentation/devicetree/bindings/video/rockchip-panel.txt b/Documentation/devicetree/bindings/video/rockchip-panel.txt
index 9fc200a..f599806 100644
--- a/Documentation/devicetree/bindings/video/rockchip-panel.txt
+++ b/Documentation/devicetree/bindings/video/rockchip-panel.txt
@@ -50,3 +50,33 @@ Example:
 		};
 
 	};
+
+Rockchip RK3288 LVDS interface
+================================
+Required properties:
+-compatible: "rockchip,rk3288-lvds";
+
+- reg: physical base address of the controller and length
+- clocks: from common clock binding: handle to dp clock.
+	of memory mapped region.
+- clock-names: from common clock binding: Shall be "pclk_lvds".
+	pclk_lvds: for power domain, if it disable soc will power down
+- rockchip,grf: this soc should set GRF regs, so need get grf here.
+- rockchip,data-mapping: should be "vesa" or "jeida"
+	This describes how the color bits are laid out in the
+	serialized LVDS signal.
+- rockchip,data-width: should be <18> or <24>
+- rockchip,panel: required a panel node
+
+Example:
+	lvds: lvds@ff96c000 {
+		compatible = "rockchip,rk3288-lvds";
+		reg = <0xff96c000 0x4000>;
+		clocks = <&cru PCLK_LVDS_PHY>;
+		clock-names = "pclk_lvds";
+
+		rockchip,grf = <&grf>;
+		rockchip,data-mapping = "jeida";
+		rockchip,data-width = <24>;
+		rockchip,panel = <&panel>;
+	};
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 7/9] drm: add Rockchip Soc rk3288 lvds connector
From: mark yao @ 2014-08-04  4:54 UTC (permalink / raw)
  To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Rob Clark, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
	David Airlie, Grant Likely, Greg Kroah-Hartman, John Stultz,
	Rom Lemarchand
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-api-u79uwXL29TY76Z2rM5mHXA, olof-nZhT3qVonbNeoWH0uzbU5w,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
	zwl-TNX95d0MmH7DzftRWevZcw, xxm-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	zhangqing-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
	wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, mark yao
In-Reply-To: <1407127274-1356-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>

Signed-off-by: mark yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 drivers/gpu/drm/rockchip/Kconfig                 |    1 +
 drivers/gpu/drm/rockchip/Makefile                |    2 +-
 drivers/gpu/drm/rockchip/connector/Kconfig       |    8 +
 drivers/gpu/drm/rockchip/connector/Makefile      |    4 +
 drivers/gpu/drm/rockchip/connector/rk3288_lvds.c |  332 ++++++++++++++++++++++
 drivers/gpu/drm/rockchip/connector/rk3288_lvds.h |   50 ++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c      |   13 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h      |    3 +
 8 files changed, 412 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/connector/Kconfig
 create mode 100644 drivers/gpu/drm/rockchip/connector/Makefile
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_lvds.c
 create mode 100644 drivers/gpu/drm/rockchip/connector/rk3288_lvds.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index ccce827..407cbb6 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -40,3 +40,4 @@ config DRM_ROCKCHIP_CONNECTOR
 	  such as lcd plane, lvds, edp , mipi, etc.
 
 source "drivers/gpu/drm/rockchip/lcdc/Kconfig"
+source "drivers/gpu/drm/rockchip/connector/Kconfig"
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 6d49edc..7d5877a 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -8,6 +8,6 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_gem.o \
 		rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_panel.o
 
-obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o
+obj-$(CONFIG_DRM_ROCKCHIP_CONNECTOR) += rockchip_drm_connector.o connector/
 obj-$(CONFIG_DRM_ROCKCHIP_LCDC) += rockchip_drm_lcdc.o lcdc/
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/connector/Kconfig b/drivers/gpu/drm/rockchip/connector/Kconfig
new file mode 100644
index 0000000..248942f
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/Kconfig
@@ -0,0 +1,8 @@
+config RK3288_LVDS
+	bool "RK3288 lvds connector support"
+	depends on DRM_ROCKCHIP_CONNECTOR
+	help
+	  Choose this option if you have a rk3288 lvds connector.
+	  rk3288 lvds transmitter support ttl rgb, lvds and dual lvds
+	  mode, dual lvds mode is support for the plane which need dual
+	  lvds channels.
diff --git a/drivers/gpu/drm/rockchip/connector/Makefile b/drivers/gpu/drm/rockchip/connector/Makefile
new file mode 100644
index 0000000..dcfbdef
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for display connector like lvds edp mipi
+#
+obj-$(CONFIG_RK3288_LVDS)	+= rk3288_lvds.o
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_lvds.c b/drivers/gpu/drm/rockchip/connector/rk3288_lvds.c
new file mode 100644
index 0000000..3ca4c6f
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_lvds.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *      mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <video/display_timing.h>
+
+#include "../rockchip_drm_connector.h"
+#include "../rockchip_drm_lcdc.h"
+#include "rk3288_lvds.h"
+
+/*
+ * @grf_offset: offset inside the grf regmap for setting the rockchip lvds
+ */
+struct rk3288_lvds_soc_data {
+	int grf_gpio1d_iomux;
+	int grf_soc_con6;
+	int grf_soc_con7;
+};
+
+struct rk3288_lvds {
+	void *base;
+	int format;
+	struct drm_display_mode mode;
+	struct device *dev;
+	void __iomem *regs;
+	struct regmap *grf;
+	struct rk3288_lvds_soc_data *soc_data;
+	struct clk *pclk;
+	bool standby;
+};
+
+static inline void lvds_writel(struct rk3288_lvds *lvds, u32 offset, u32 val)
+{
+	writel_relaxed(val, lvds->regs + offset);
+	writel_relaxed(val, lvds->regs + offset + 0x100);
+}
+
+static inline int lvds_name_to_format(const char *s)
+{
+	if (!s)
+		return 0;
+
+	if (strncmp(s, "jeida", 6) == 0)
+		return LVDS_FORMAT_JEIDA;
+	else if (strncmp(s, "vesa", 6) == 0)
+		return LVDS_FORMAT_VESA;
+
+	return 0;
+}
+
+static void rk3288_lvds_disable(struct rockchip_connector *conn)
+{
+	struct rk3288_lvds *lvds = conn->priv;
+	int ret = 0;
+
+	if (lvds->standby)
+		return;
+
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, 0xffff8000);
+	if (ret != 0)
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+	/* disable tx */
+	writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_21);
+	/* disable pll */
+	writel_relaxed(0xff, lvds->regs + LVDS_CFG_REG_C);
+
+	clk_disable_unprepare(lvds->pclk);
+	lvds->standby = true;
+}
+
+static void rk3288_lvds_en(struct rockchip_connector *conn)
+{
+	struct rk3288_lvds *lvds = conn->priv;
+	struct drm_display_mode *mode = &lvds->mode;
+	u32 val = 0;
+	u32 h_bp = mode->htotal - mode->hsync_start;
+	u8 pin_hsync = (conn->flags & DISPLAY_FLAGS_HSYNC_HIGH) ? 1 : 0;
+	u8 pin_den = (conn->flags & DISPLAY_FLAGS_DE_HIGH) ? 1 : 0;
+	u8 pin_dclk = (conn->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) ? 1 : 0;
+	u8 lvds_format = lvds->format;
+	u8 type = conn->type;
+	int lcdc_id = 1;
+	int ret = 0;
+
+	if (!lvds->standby)
+		return;
+
+	/* enable clk */
+	ret = clk_prepare_enable(lvds->pclk);
+	if (ret < 0) {
+		dev_err(lvds->dev, "failed to enable lvds pclk %d\n", ret);
+		return;
+	}
+	/* lcdc1 = vop little, lcdc0 = vop big */
+	if (lcdc_id == 1)
+		val = LVDS_SEL_VOP_LIT | (LVDS_SEL_VOP_LIT << 16);
+	else
+		val = LVDS_SEL_VOP_LIT << 16;
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val);
+	if (ret != 0) {
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+
+	val = lvds_format;
+	if (type == ROCKCHIP_DISPLAY_TYPE_DUAL_LVDS)
+		val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN;
+	else if (type == ROCKCHIP_DISPLAY_TYPE_LVDS)
+		val |= LVDS_CH0_EN;
+	else if (type == ROCKCHIP_DISPLAY_TYPE_RGB)
+		val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN;
+
+	if (h_bp & 0x01)
+		val |= LVDS_START_PHASE_RST_1;
+
+	val |= (pin_dclk << 8) | (pin_hsync << 9) |
+		(pin_den << 10);
+	val |= (0xffff << 16);
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val);
+	if (ret != 0) {
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+
+	if (type == ROCKCHIP_DISPLAY_TYPE_RGB) {
+		val = 0x007f007f;
+		ret = regmap_write(lvds->grf, lvds->soc_data->grf_gpio1d_iomux,
+				   val);
+		if (ret != 0) {
+			dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+			return;
+		}
+
+		lvds_writel(lvds, LVDS_CH0_REG_0, 0x7f);
+		lvds_writel(lvds, LVDS_CH0_REG_1, 0x40);
+		lvds_writel(lvds, LVDS_CH0_REG_2, 0x00);
+
+		lvds_writel(lvds, LVDS_CH0_REG_4, 0x3f);
+		lvds_writel(lvds, LVDS_CH0_REG_5, 0x3f);
+		lvds_writel(lvds, LVDS_CH0_REG_3, 0x46);
+		lvds_writel(lvds, LVDS_CH0_REG_D, 0x0a);
+		lvds_writel(lvds, LVDS_CH0_REG_20, 0x44);
+		writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_C);
+		writel_relaxed(0x92, lvds->regs + LVDS_CFG_REG_21);
+
+		lvds_writel(lvds, 0x100, 0x7f);
+		lvds_writel(lvds, 0x104, 0x40);
+		lvds_writel(lvds, 0x108, 0x00);
+		lvds_writel(lvds, 0x10c, 0x46);
+		lvds_writel(lvds, 0x110, 0x3f);
+		lvds_writel(lvds, 0x114, 0x3f);
+		lvds_writel(lvds, 0x134, 0x0a);
+	} else {
+		lvds_writel(lvds, LVDS_CH0_REG_0, 0xbf);
+		lvds_writel(lvds, LVDS_CH0_REG_1, 0x3f);
+		lvds_writel(lvds, LVDS_CH0_REG_2, 0xfe);
+		lvds_writel(lvds, LVDS_CH0_REG_3, 0x46);
+		lvds_writel(lvds, LVDS_CH0_REG_4, 0x00);
+		lvds_writel(lvds, LVDS_CH0_REG_D, 0x0a);
+		lvds_writel(lvds, LVDS_CH0_REG_20, 0x44);
+		writel_relaxed(0x00, lvds->regs + LVDS_CFG_REG_C);
+		writel_relaxed(0x92, lvds->regs + LVDS_CFG_REG_21);
+	}
+
+	lvds->standby = false;
+}
+
+static int rk3288_lvds_setmode(struct rockchip_connector *conn,
+			       struct drm_display_mode *mode)
+{
+	struct rk3288_lvds *lvds = conn->priv;
+
+	memcpy(&lvds->mode, mode, sizeof(*mode));
+
+	return 0;
+}
+
+static struct rockchip_connector lvds_conn = {
+	.enable = rk3288_lvds_en,
+	.disable = rk3288_lvds_disable,
+	.setmode = rk3288_lvds_setmode,
+};
+
+static struct rk3288_lvds_soc_data soc_data[2] = {
+	{.grf_gpio1d_iomux = 0x000c,
+	 .grf_soc_con6 = 0x025c,
+	 .grf_soc_con7 = 0x0260},
+	{.grf_gpio1d_iomux = -1,
+	 .grf_soc_con6 = -1,
+	/* no lvds switching needed */
+	 .grf_soc_con7 = -1},
+};
+
+static const struct of_device_id rk3288_lvds_dt_ids[] = {
+	{.compatible = "rockchip,rk3288-lvds",
+	 .data = (void *)&soc_data[0] },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids);
+
+static int rk3288_lvds_probe(struct platform_device *pdev)
+{
+	struct rk3288_lvds *lvds;
+	struct resource *res;
+	struct device_node *np = pdev->dev.of_node;
+	const char *name;
+	const struct of_device_id *match;
+	u32 i;
+
+	if (!np) {
+		dev_err(&pdev->dev, "Missing device tree node.\n");
+		return -EINVAL;
+	}
+
+	lvds = devm_kzalloc(&pdev->dev, sizeof(struct rk3288_lvds), GFP_KERNEL);
+	if (!lvds) {
+		dev_err(&pdev->dev, "no memory for state\n");
+		return -ENOMEM;
+	}
+
+	match = of_match_node(rk3288_lvds_dt_ids, np);
+	lvds->soc_data = (struct rk3288_lvds_soc_data *)match->data;
+	/*
+	 * The control bit is located in the GRF register space.
+	 */
+	if (lvds->soc_data->grf_gpio1d_iomux >= 0) {
+		lvds->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+		if (IS_ERR(lvds->grf)) {
+			dev_err(&pdev->dev,
+				"rockchip-lvds needs rockchip,grf property\n");
+			return PTR_ERR(lvds->grf);
+		}
+	}
+
+	if (of_property_read_string(np, "rockchip,data-mapping", &name))
+		/* default set it as format jeida */
+		lvds->format = LVDS_FORMAT_JEIDA;
+	else
+		lvds->format = lvds_name_to_format(name);
+
+	if (of_property_read_u32(np, "rockchip,data-width", &i)) {
+		lvds->format |= LVDS_24BIT;
+	} else {
+		if (i == 24) {
+			lvds->format |= LVDS_24BIT;
+		} else if (i == 18) {
+			lvds->format |= LVDS_18BIT;
+		} else {
+			dev_err(&pdev->dev,
+				"rockchip-lvds unsupport data-width[%d]\n", i);
+			return -EINVAL;
+		}
+	}
+
+	lvds->dev = &pdev->dev;
+
+	lvds_conn.priv = lvds;
+	lvds_conn.dev = &pdev->dev;
+	lvds_conn.type = ROCKCHIP_DISPLAY_TYPE_LVDS;
+	lvds->base = rockchip_connector_register(&lvds_conn);
+	if (!lvds->base)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lvds->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(lvds->regs)) {
+		dev_err(&pdev->dev, "ioremap reg failed\n");
+		return PTR_ERR(lvds->regs);
+	}
+
+	lvds->pclk = devm_clk_get(&pdev->dev, "pclk_lvds");
+	if (IS_ERR(lvds->pclk)) {
+		dev_err(&pdev->dev, "get clk failed\n");
+		return PTR_ERR(lvds->pclk);
+	}
+
+	lvds->standby = true;
+
+	platform_set_drvdata(pdev, lvds);
+	dev_set_name(lvds->dev, "rk3288-lvds");
+
+	dev_info(&pdev->dev, "rk3288 lvds driver probe success\n");
+
+	return 0;
+}
+
+static int rk3288_lvds_remove(struct platform_device *pdev)
+{
+	struct rk3288_lvds *lvds = lvds_conn.priv;
+
+	rk3288_lvds_disable(&lvds_conn);
+	rockchip_connector_unregister(lvds->base);
+
+	return 0;
+}
+
+struct platform_driver rk3288_lvds_driver = {
+	.probe = rk3288_lvds_probe,
+	.remove = rk3288_lvds_remove,
+	.driver = {
+		   .name = "rk3288-lvds",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(rk3288_lvds_dt_ids),
+	},
+};
diff --git a/drivers/gpu/drm/rockchip/connector/rk3288_lvds.h b/drivers/gpu/drm/rockchip/connector/rk3288_lvds.h
new file mode 100644
index 0000000..61c71cc
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/connector/rk3288_lvds.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *      mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * 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 _RK3288_LVDS_
+#define _RK3288_LVDS_
+
+#define LVDS_CH0_REG_0			0x00
+#define LVDS_CH0_REG_1			0x04
+#define LVDS_CH0_REG_2			0x08
+#define LVDS_CH0_REG_3			0x0c
+#define LVDS_CH0_REG_4			0x10
+#define LVDS_CH0_REG_5			0x14
+#define LVDS_CH0_REG_9			0x24
+#define LVDS_CFG_REG_C			0x30
+#define LVDS_CH0_REG_D			0x34
+#define LVDS_CH0_REG_F			0x3c
+#define LVDS_CH0_REG_20			0x80
+#define LVDS_CFG_REG_21			0x84
+
+#define LVDS_SEL_VOP_LIT		(1 << 3)
+
+#define LVDS_FMT_MASK			(0x07 << 16)
+#define LVDS_MSB			(0x01 << 3)
+#define LVDS_DUAL			(0x01 << 4)
+#define LVDS_FMT_1			(0x01 << 5)
+#define LVDS_TTL_EN			(0x01 << 6)
+#define LVDS_START_PHASE_RST_1		(0x01 << 7)
+#define LVDS_DCLK_INV			(0x01 << 8)
+#define LVDS_CH0_EN			(0x01 << 11)
+#define LVDS_CH1_EN			(0x01 << 12)
+#define LVDS_PWRDN			(0x01 << 15)
+
+#define LVDS_24BIT		(0 << 1)
+#define LVDS_18BIT		(1 << 1)
+#define LVDS_FORMAT_VESA	(0 << 0)
+#define LVDS_FORMAT_JEIDA	(1 << 0)
+#endif /* _RK3288_LVDS_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 4871867..59187aa 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -558,6 +558,12 @@ static int rockchip_drm_init(void)
 		goto out_lcdc;
 #endif
 
+#ifdef CONFIG_RK3288_LVDS
+	ret = platform_driver_register(&rk3288_lvds_driver);
+	if (ret)
+		goto out_lvds;
+#endif
+
 	rockchip_drm_pdev = platform_device_register_simple("rockchip-drm", -1,
 							    NULL, 0);
 	if (IS_ERR(rockchip_drm_pdev)) {
@@ -574,6 +580,10 @@ static int rockchip_drm_init(void)
 out_drm_driver:
 	platform_device_unregister(rockchip_drm_pdev);
 out_drm_pdev:
+#ifdef CONFIG_RK3288_LVDS
+	platform_driver_unregister(&rk3288_lvds_driver);
+out_lvds:
+#endif
 #ifdef CONFIG_DRM_ROCKCHIP_LCDC
 	platform_driver_unregister(&rockchip_lcdc_platform_driver);
 out_lcdc:
@@ -586,6 +596,9 @@ static void rockchip_drm_exit(void)
 {
 	platform_device_unregister(rockchip_drm_pdev);
 	platform_driver_unregister(&rockchip_drm_platform_driver);
+#ifdef CONFIG_RK3288_LVDS
+	platform_driver_unregister(&rk3288_lvds_driver);
+#endif
 #ifdef CONFIG_DRM_ROCKCHIP_LCDC
 	platform_driver_unregister(&rockchip_lcdc_platform_driver);
 #endif
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index c0c1d89..d28f4dc 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -125,4 +125,7 @@ extern struct platform_driver rockchip_panel_platform_driver;
 #ifdef CONFIG_DRM_ROCKCHIP_LCDC
 extern struct platform_driver rockchip_lcdc_platform_driver;
 #endif
+#ifdef CONFIG_RK3288_LVDS
+extern struct platform_driver rk3288_lvds_driver;
+#endif
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 8/9] Add devicetree bindings for Rockchip Soc EDP
From: mark yao @ 2014-08-04  4:55 UTC (permalink / raw)
  To: heiko, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, David Airlie,
	Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
  Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api, olof,
	djkurtz, xjq, kfx, cym, cf, zyw, zwl, xxm, huangtao, kever.yang,
	zhangqing, yxj, wxt, xw, mark yao
In-Reply-To: <1407127274-1356-1-git-send-email-mark.yao@rock-chips.com>

Signed-off-by: mark yao <yzq@rock-chips.com>
---
 .../devicetree/bindings/video/rockchip-panel.txt   |   34 ++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/Documentation/devicetree/bindings/video/rockchip-panel.txt b/Documentation/devicetree/bindings/video/rockchip-panel.txt
index f599806..f6d80f6 100644
--- a/Documentation/devicetree/bindings/video/rockchip-panel.txt
+++ b/Documentation/devicetree/bindings/video/rockchip-panel.txt
@@ -80,3 +80,37 @@ Example:
 		rockchip,data-width = <24>;
 		rockchip,panel = <&panel>;
 	};
+
+Rockchip RK3288 EDP interface
+================================
+Required properties:
+-compatible: "rockchip,rk3288-edp";
+
+- reg: physical base address of the controller and length
+- clocks: from common clock binding: handle to dp clock.
+	of memory mapped region.
+- clock-names: from common clock binding:
+	Required elements: "clk_edp"
+			"clk_edp_24m"
+			"clk_edp_24m_parent"
+			"pclk_edp"
+- rockchip,grf: this soc should set GRF regs, so need get grf here.
+- resets: Must contain an entry for each entry in reset-names.
+	See ../reset/reset.txt for details.
+- reset-names: Must include the following entries:
+  - edp
+- rockchip,panel: required a panel node
+
+Example:
+	edp: edp@ff970000 {
+		compatible = "rockchip,rk3288-edp";
+                reg = <0xff970000 0x4000>;
+                interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+                clocks = <&cru SCLK_EDP>, <&cru SCLK_EDP_24M>, <&cru PCLK_EDP_CTRL>, <&xin24m>;
+                clock-names = "clk_edp", "clk_edp_24m", "pclk_edp", "clk_edp_24m_parent";
+
+                rockchip,grf = <&grf>;
+                resets = <&cru 111>;
+                reset-names = "edp";
+		rockchip,panel = <&panel>;
+	};
-- 
1.7.9.5



^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox