* [PATCH] usb: gadget: f_rndis: fix usb_interface_descriptor for rndis
From: Heiko Schocher @ 2014-09-24 6:48 UTC (permalink / raw)
To: linux-usb-u79uwXL29TY76Z2rM5mHXA
Cc: Heiko Schocher, Felipe Balbi, Greg Kroah-Hartman,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Oliver Neukum,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-api-u79uwXL29TY76Z2rM5mHXA,
Andrzej Pietrasiewicz, Michal Nazarewicz, Kyungmin Park,
Dan Carpenter, Macpaul Lin
use the values for RNDIS over Ethernet as defined in
http://www.usb.org/developers/defined_class
(search for RDNIS):
- baseclass: 0xef (miscellaneous)
- subclass: 0x04
- protocol: 0x01
with this setings the file in Documentation/usb/linux.inf is
obsolete.
Signed-off-by: Heiko Schocher <hs-ynQEQJNshbs@public.gmane.org>
---
Cc: Felipe Balbi <balbi-l0cyMroinI0@public.gmane.org>
Cc: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org>
Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Oliver Neukum <oliver-Q6YOFhsQ4GZ7tPAFqOLdPg@public.gmane.org>
Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Andrzej Pietrasiewicz <andrzej.p-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Cc: Michal Nazarewicz <mina86-deATy8a+UHjQT0dZR+AlfA@public.gmane.org>
Cc: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Cc: Dan Carpenter <dan.carpenter-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org>
Cc: Macpaul Lin <macpaul-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Tested with the "USB Compliance test suite which runs Windows", see:
http://www.usb.org/developers/tools/usb20_tools/#usb20cv
drivers/net/usb/cdc_ether.c | 6 +++---
drivers/usb/core/generic.c | 6 +++---
drivers/usb/gadget/function/f_rndis.c | 6 +++---
include/uapi/linux/usb/cdc.h | 3 +++
4 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 2a32d91..9c216c2 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -35,9 +35,9 @@
static int is_rndis(struct usb_interface_descriptor *desc)
{
- return (desc->bInterfaceClass == USB_CLASS_COMM &&
- desc->bInterfaceSubClass == 2 &&
- desc->bInterfaceProtocol == 0xff);
+ return (desc->bInterfaceClass == USB_CLASS_MISC &&
+ desc->bInterfaceSubClass == USB_CDC_SUBCLASS_RNDIS &&
+ desc->bInterfaceProtocol == USB_CDC_RNDIS_PROTO_ETH);
}
static int is_activesync(struct usb_interface_descriptor *desc)
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 358ca8d..a2a4e05 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -28,9 +28,9 @@ static inline const char *plural(int n)
static int is_rndis(struct usb_interface_descriptor *desc)
{
- return desc->bInterfaceClass == USB_CLASS_COMM
- && desc->bInterfaceSubClass == 2
- && desc->bInterfaceProtocol == 0xff;
+ return desc->bInterfaceClass == USB_CLASS_MISC
+ && desc->bInterfaceSubClass == USB_CDC_SUBCLASS_RNDIS
+ && desc->bInterfaceProtocol == USB_CDC_RNDIS_PROTO_ETH;
}
static int is_activesync(struct usb_interface_descriptor *desc)
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index ddb09dc..cc06046 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -117,9 +117,9 @@ static struct usb_interface_descriptor rndis_control_intf = {
/* .bInterfaceNumber = DYNAMIC */
/* status endpoint is optional; this could be patched later */
.bNumEndpoints = 1,
- .bInterfaceClass = USB_CLASS_COMM,
- .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
- .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
+ .bInterfaceClass = USB_CLASS_MISC,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_RNDIS,
+ .bInterfaceProtocol = USB_CDC_RNDIS_PROTO_ETH,
/* .iInterface = DYNAMIC */
};
diff --git a/include/uapi/linux/usb/cdc.h b/include/uapi/linux/usb/cdc.h
index b6a9cdd..8e8fc85 100644
--- a/include/uapi/linux/usb/cdc.h
+++ b/include/uapi/linux/usb/cdc.h
@@ -12,6 +12,7 @@
#include <linux/types.h>
#define USB_CDC_SUBCLASS_ACM 0x02
+#define USB_CDC_SUBCLASS_RNDIS 0x04
#define USB_CDC_SUBCLASS_ETHERNET 0x06
#define USB_CDC_SUBCLASS_WHCM 0x08
#define USB_CDC_SUBCLASS_DMM 0x09
@@ -31,6 +32,8 @@
#define USB_CDC_ACM_PROTO_AT_CDMA 6
#define USB_CDC_ACM_PROTO_VENDOR 0xff
+#define USB_CDC_RNDIS_PROTO_ETH 1
+
#define USB_CDC_PROTO_EEM 7
#define USB_CDC_NCM_PROTO_NTB 1
--
1.8.3.1
^ permalink raw reply related
* Re: [PATCH v4 04/11] drivers: base: support cpu cache information interface to userspace via sysfs
From: Greg Kroah-Hartman @ 2014-09-24 6:35 UTC (permalink / raw)
To: Sudeep Holla
Cc: LKML, Heiko Carstens, Lorenzo Pieralisi, Stephen Boyd,
linux-api@vger.kernel.org, linux390@de.ibm.com,
linux-arm-kernel@lists.infradead.org, linux-ia64@vger.kernel.org,
linuxppc-dev@lists.ozlabs.org, linux-s390@vger.kernel.org,
x86@kernel.org
In-Reply-To: <20140917190048.GA4063@kroah.com>
On Wed, Sep 17, 2014 at 12:00:48PM -0700, Greg Kroah-Hartman wrote:
> On Wed, Sep 17, 2014 at 06:25:10PM +0100, Sudeep Holla wrote:
> > Hi Greg,
> >
> > On 03/09/14 18:00, Sudeep Holla wrote:
> > >From: Sudeep Holla <sudeep.holla@arm.com>
> > >
> > >This patch adds initial support for providing processor cache information
> > >to userspace through sysfs interface. This is based on already existing
> > >implementations(x86, ia64, s390 and powerpc) and hence the interface is
> > >intended to be fully compatible.
> > >
> > >The main purpose of this generic support is to avoid further code
> > >duplication to support new architectures and also to unify all the existing
> > >different implementations.
> > >
> > >This implementation maintains the hierarchy of cache objects which reflects
> > >the system's cache topology. Cache devices are instantiated as needed as
> > >CPUs come online. The cache information is replicated per-cpu even if they are
> > >shared. A per-cpu array of cache information maintained is used mainly for
> > >sysfs-related book keeping.
> > >
> > >It also implements the shared_cpu_map attribute, which is essential for
> > >enabling both kernel and user-space to discover the system's overall cache
> > >topology.
> > >
> > >This patch also add the missing ABI documentation for the cacheinfo sysfs
> > >interface already, which is well defined and widely used.
> > >
> >
> > Can you review the first 4 patches in this series please ?
>
> It's in my todo queue, which is really long at the moment due to me
> going to conferences (at one right now...) Will be working on this
> soon, thanks for your patience.
Based on the review comments, I think you are going to change at least
the first patch, right? Please resend the latest version of this
series, with all of the accumulated tested-by and acked lines and
resend.
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH v2 2/2] perf: Userspace event
From: Namhyung Kim @ 2014-09-24 6:07 UTC (permalink / raw)
To: Pawel Moll
Cc: Richard Cochran, Steven Rostedt, Ingo Molnar, Peter Zijlstra,
Paul Mackerras, Arnaldo Carvalho de Melo, John Stultz,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1411491787-25938-3-git-send-email-pawel.moll-5wv7dgnIgG8@public.gmane.org>
On Tue, 23 Sep 2014 18:03:07 +0100, Pawel Moll wrote:
> This patch adds a new PERF_COUNT_SW_UEVENT software event
> and a related PERF_SAMPLE_UEVENT sample. User can now
> write to the the perf file descriptor, injecting such
> event in the perf buffer.
It seems the PERF_SAMPLE_UEVENT sample can be injected to any event. So
why the PERF_COUNT_SW_UEVENT is needed? At least one can use the
SW_DUMMY event for that purpose.
Also I think it'd be better to be a record type (PERF_RECORD_XXX)
instead of a sample flag (PERF_SAMPLE_XXX). In perf tools, we already
use perf_user_event_type for synthesized userspace events. This way it
can avoid unnecessary sample processing for userspace events.
For contents, I prefer to give complete control to users - kernel
doesn't need to care about it other than its size. If one just wants to
use strings only, she can write them directly. If others want to mix
different types of data, they might need to define a data format for
their use.
Thanks,
Namhyung
>
> The UEVENT sample begins with a 32 bit unsigned integer
> value describing type of the generated event. The type
> can be set with PERF_EVENT_IOC_SET_UEVENT_TYPE ioctl
> (zero is the default value). Then follows the 32 bit
> unsigned size of the data (provided as the "count" argument
> of the write syscall) and the data itself plus padding
> aligning the overall sample size to 8 bytes.
>
> Data Events with type equal 0 are defined as zero-terminated
> strings, other types are defined by userspace (the perf tool
> will contain a list of known values with reference
> implementation of data content parsers).
>
> Possible use cases for this feature:
>
> - "perf_printf" like mechanism to add logging messages
> to one's perf session; in the simplest case it can be just
>
> uint32_t type = 0;
> ioctl(perf_fd, PERF_EVENT_IOC_SET_UEVENT_TYPE, &type);
> dprintf(perf_fd, "Message");
>
> (note that dprintf does *not* write the terminating '\0'; for
> users' convenience kernel add it when type is set to zero)
>
> - "perf_printf" used by for perf trace tool,
> where certain traced process' calls are intercepted
> (eg. using LD_PRELOAD) and treated as logging
> requests, with it output redirected into the
> perf buffer
>
> - synchronisation of performance data generated in
> user space with the perf stream coming from the kernel.
> For example, the marker can be inserted by a JIT engine
> after it generated portion of the code, but before the
> code is executed for the first time, allowing the
> post-processor to pick the correct debugging
> information.
>
> - other example is a system profiling tool taking data
> from other sources than just perf, which generates a marker
> at the beginning at at the end of the session
> (also possibly periodically during the session) to
> synchronise kernel timestamps with clock values
> obtained in userspace (gtod or raw_monotonic).
>
> Signed-off-by: Pawel Moll <pawel.moll-5wv7dgnIgG8@public.gmane.org>
> ---
> Changes since v1:
>
> - replaced ioctl-based interface with write syscall
> (there's still a ioctl to set an event type)
>
> - replaced all "USERSPACE_EVENT" and alike strings
> with much shorter "UEVENT"
>
> include/linux/perf_event.h | 14 ++++++++
> include/uapi/linux/perf_event.h | 24 ++++++++++++-
> kernel/events/core.c | 80 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 117 insertions(+), 1 deletion(-)
>
> diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
> index 28b73b2..c130579 100644
> --- a/include/linux/perf_event.h
> +++ b/include/linux/perf_event.h
> @@ -64,6 +64,12 @@ struct perf_raw_record {
> void *data;
> };
>
> +struct perf_uevent {
> + u32 type;
> + u32 size;
> + u8 data[0];
> +};
> +
> /*
> * branch stack layout:
> * nr: number of taken branches stored in entries[]
> @@ -433,6 +439,7 @@ struct perf_event {
>
> struct pid_namespace *ns;
> u64 id;
> + u32 uevent_type;
>
> perf_overflow_handler_t overflow_handler;
> void *overflow_handler_context;
> @@ -604,6 +611,8 @@ struct perf_sample_data {
> u64 txn;
> /* Raw monotonic timestamp, for userspace time correlation */
> u64 clock_raw_monotonic;
> + /* Userspace-originating event */
> + struct perf_uevent *uevent;
> };
>
> static inline void perf_sample_data_init(struct perf_sample_data *data,
> @@ -685,6 +694,9 @@ perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)
> }
> }
>
> +int perf_uevent_write(struct perf_event *event, u32 type, u32 size,
> + const char __user *data);
> +
> extern struct static_key_deferred perf_sched_events;
>
> static inline void perf_event_task_sched_in(struct task_struct *prev,
> @@ -807,6 +819,8 @@ static inline int perf_event_refresh(struct perf_event *event, int refresh)
>
> static inline void
> perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) { }
> +static inline int perf_uevent_write(struct perf_event *event, u32 type,
> + u32 size, const char __user *data) { return -EINVAL; }
> static inline void
> perf_bp_event(struct perf_event *event, void *data) { }
>
> diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
> index e5a75c5..1fabc2c 100644
> --- a/include/uapi/linux/perf_event.h
> +++ b/include/uapi/linux/perf_event.h
> @@ -110,6 +110,7 @@ enum perf_sw_ids {
> PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
> PERF_COUNT_SW_EMULATION_FAULTS = 8,
> PERF_COUNT_SW_DUMMY = 9,
> + PERF_COUNT_SW_UEVENT = 10,
>
> PERF_COUNT_SW_MAX, /* non-ABI */
> };
> @@ -138,8 +139,9 @@ enum perf_event_sample_format {
> PERF_SAMPLE_IDENTIFIER = 1U << 16,
> PERF_SAMPLE_TRANSACTION = 1U << 17,
> PERF_SAMPLE_CLOCK_RAW_MONOTONIC = 1U << 18,
> + PERF_SAMPLE_UEVENT = 1U << 19,
>
> - PERF_SAMPLE_MAX = 1U << 19, /* non-ABI */
> + PERF_SAMPLE_MAX = 1U << 20, /* non-ABI */
> };
>
> /*
> @@ -350,6 +352,7 @@ struct perf_event_attr {
> #define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
> #define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
> #define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)
> +#define PERF_EVENT_IOC_SET_UEVENT_TYPE _IOW('$', 8, __u32)
>
> enum perf_event_ioc_flags {
> PERF_IOC_FLAG_GROUP = 1U << 0,
> @@ -688,6 +691,25 @@ enum perf_event_type {
> * { u64 data_src; } && PERF_SAMPLE_DATA_SRC
> * { u64 transaction; } && PERF_SAMPLE_TRANSACTION
> * { u64 clock_raw_monotonic; } && PERF_SAMPLE_CLOCK_RAW_MONOTONIC
> + *
> + * #
> + * # Contents of UEVENT sample data depend on its type.
> + * #
> + * # Type 0 means that the data is a zero-terminated string that
> + * # can be printf-ed in the normal way.
> + * #
> + * # Meaning of other type values depends on the userspace
> + * # and the perf tool code contains a list of those with
> + * # reference implementations of parsers.
> + * #
> + * # Overall size of the sample (including type and size fields)
> + * # is always aligned to 8 bytes by adding padding after
> + * # the data.
> + * #
> + * { u32 type;
> + * u32 size;
> + * char data[size];
> + * char __padding[] } && PERF_SAMPLE_UEVENT
> * };
> */
> PERF_RECORD_SAMPLE = 9,
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index f6df547..69ca8c9 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -3526,6 +3526,15 @@ perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
> return perf_read_hw(event, buf, count);
> }
>
> +static ssize_t
> +perf_write(struct file *file, const char __user *buf, size_t count,
> + loff_t *ppos)
> +{
> + struct perf_event *event = file->private_data;
> +
> + return perf_uevent_write(event, event->uevent_type, count, buf);
> +}
> +
> static unsigned int perf_poll(struct file *file, poll_table *wait)
> {
> struct perf_event *event = file->private_data;
> @@ -3636,6 +3645,17 @@ unlock:
> return ret;
> }
>
> +static int perf_event_set_uevent_type(struct perf_event *event, u32 __user *arg)
> +{
> + if (!arg)
> + return -EINVAL;
> +
> + if (copy_from_user(&event->uevent_type, arg, sizeof(*arg)))
> + return -EFAULT;
> +
> + return 0;
> +}
> +
> static const struct file_operations perf_fops;
>
> static inline int perf_fget_light(int fd, struct fd *p)
> @@ -3709,6 +3729,9 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> case PERF_EVENT_IOC_SET_FILTER:
> return perf_event_set_filter(event, (void __user *)arg);
>
> + case PERF_EVENT_IOC_SET_UEVENT_TYPE:
> + return perf_event_set_uevent_type(event, (u32 __user *)arg);
> +
> default:
> return -ENOTTY;
> }
> @@ -4244,6 +4267,7 @@ static const struct file_operations perf_fops = {
> .llseek = no_llseek,
> .release = perf_release,
> .read = perf_read,
> + .write = perf_write,
> .poll = perf_poll,
> .unlocked_ioctl = perf_ioctl,
> .compat_ioctl = perf_compat_ioctl,
> @@ -4727,6 +4751,16 @@ void perf_output_sample(struct perf_output_handle *handle,
> if (sample_type & PERF_SAMPLE_CLOCK_RAW_MONOTONIC)
> perf_output_put(handle, data->clock_raw_monotonic);
>
> + if (sample_type & PERF_SAMPLE_UEVENT) {
> + int size = data->uevent->size;
> + int padding = ALIGN(size, sizeof(u64)) - size;
> +
> + perf_output_put(handle, data->uevent->type);
> + perf_output_put(handle, size);
> + __output_copy(handle, data->uevent->data, size);
> + perf_output_skip(handle, padding);
> + };
> +
> if (!event->attr.watermark) {
> int wakeup_events = event->attr.wakeup_events;
>
> @@ -4834,6 +4868,10 @@ void perf_prepare_sample(struct perf_event_header *header,
> data->stack_user_size = stack_size;
> header->size += size;
> }
> +
> + if (sample_type & PERF_SAMPLE_UEVENT)
> + header->size += sizeof(u32) + sizeof(u32) +
> + ALIGN(data->uevent->size, sizeof(u64));
> }
>
> static void perf_event_output(struct perf_event *event,
> @@ -5961,6 +5999,48 @@ static struct pmu perf_swevent = {
> .event_idx = perf_swevent_event_idx,
> };
>
> +int perf_uevent_write(struct perf_event *event, u32 type, u32 size,
> + const char __user *data)
> +{
> + struct perf_uevent *uevent;
> + struct perf_sample_data sample;
> + struct pt_regs *regs = current_pt_regs();
> +
> + /* Need some sane limit */
> + if (size > PAGE_SIZE)
> + return -EFBIG;
> +
> + /*
> + * Type 0 means zero-terminated string, but standard dprintf()
> + * doesn't write the zero character. Let's allocate one more byte
> + * for such event...
> + */
> + uevent = kmalloc(sizeof(*uevent) + size + (type == 0 ? 1 : 0),
> + GFP_KERNEL);
> + if (!uevent)
> + return -ENOMEM;
> +
> + if (copy_from_user(uevent->data, data, size)) {
> + kfree(uevent);
> + return -EFAULT;
> + }
> +
> + /* ... and then zero it, if necessary. */
> + if (type == 0 && uevent->data[size - 1])
> + uevent->data[size++] = '\0';
> +
> + uevent->type = type;
> + uevent->size = size;
> +
> + perf_sample_data_init(&sample, 0, 0);
> + sample.uevent = uevent;
> + perf_event_output(event, &sample, regs);
> +
> + kfree(uevent);
> +
> + return size;
> +}
> +
> #ifdef CONFIG_EVENT_TRACING
>
> static int perf_tp_filter_match(struct perf_event *event,
^ permalink raw reply
* Re: [PATCH v2 1/2] perf: Add sampling of the raw monotonic clock
From: Namhyung Kim @ 2014-09-24 5:41 UTC (permalink / raw)
To: Pawel Moll
Cc: Richard Cochran, Steven Rostedt, Ingo Molnar, Peter Zijlstra,
Paul Mackerras, Arnaldo Carvalho de Melo, John Stultz,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1411491787-25938-2-git-send-email-pawel.moll-5wv7dgnIgG8@public.gmane.org>
Hi Pawel,
On Tue, 23 Sep 2014 18:03:06 +0100, Pawel Moll wrote:
> This patch adds an option to sample raw monotonic clock
> value with any perf event, with the the aim of allowing
> time correlation between data coming from perf and
> additional performance-related information generated in
> userspace.
>
> In order to correlate timestamps in perf data stream
> with events happening in userspace (be it JITed debug
> symbols or hwmon-originating environment data), user
> requests a more or less periodic event (sched_switch
> trace event of a hrtimer-based cpu-clock being the
> most obvious examples) with PERF_SAMPLE_TIME *and*
> PERF_SAMPLE_CLOCK_RAW_MONOTONIC and stamps
> user-originating data with values obtained from
> clock_gettime(CLOCK_MONOTONIC_RAW). Then, during
> analysis, one looks at the perf events immediately
> preceding and following (in terms of the
> clock_raw_monotonic sample) the userspace event and
> does simple linear approximation to get the equivalent
> perf time.
>
> perf event user event
> -----O--------------+-------------O------> t_mono
> : | :
> : V :
> -----O----------------------------O------> t_perf
Probably a dumb question: why not make PERF_SAMPLE_TIME being monotonic
clock instead of adding a new PERF_SAMPLE_CLOCK_XXX flag? Maybe we can
add a new ioctl command like PERF_EVENT_IOC_SET_CLOCK so that one can
pass a clock id.
Thanks,
Namhyung
>
> Signed-off-by: Pawel Moll <pawel.moll-5wv7dgnIgG8@public.gmane.org>
> ---
> Changes since v1:
>
> - none
>
> include/linux/perf_event.h | 2 ++
> include/uapi/linux/perf_event.h | 4 +++-
> kernel/events/core.c | 13 +++++++++++++
> 3 files changed, 18 insertions(+), 1 deletion(-)
>
> diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
> index 707617a..28b73b2 100644
> --- a/include/linux/perf_event.h
> +++ b/include/linux/perf_event.h
> @@ -602,6 +602,8 @@ struct perf_sample_data {
> * Transaction flags for abort events:
> */
> u64 txn;
> + /* Raw monotonic timestamp, for userspace time correlation */
> + u64 clock_raw_monotonic;
> };
>
> static inline void perf_sample_data_init(struct perf_sample_data *data,
> diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
> index 9269de2..e5a75c5 100644
> --- a/include/uapi/linux/perf_event.h
> +++ b/include/uapi/linux/perf_event.h
> @@ -137,8 +137,9 @@ enum perf_event_sample_format {
> PERF_SAMPLE_DATA_SRC = 1U << 15,
> PERF_SAMPLE_IDENTIFIER = 1U << 16,
> PERF_SAMPLE_TRANSACTION = 1U << 17,
> + PERF_SAMPLE_CLOCK_RAW_MONOTONIC = 1U << 18,
>
> - PERF_SAMPLE_MAX = 1U << 18, /* non-ABI */
> + PERF_SAMPLE_MAX = 1U << 19, /* non-ABI */
> };
>
> /*
> @@ -686,6 +687,7 @@ enum perf_event_type {
> * { u64 weight; } && PERF_SAMPLE_WEIGHT
> * { u64 data_src; } && PERF_SAMPLE_DATA_SRC
> * { u64 transaction; } && PERF_SAMPLE_TRANSACTION
> + * { u64 clock_raw_monotonic; } && PERF_SAMPLE_CLOCK_RAW_MONOTONIC
> * };
> */
> PERF_RECORD_SAMPLE = 9,
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index f9c1ed0..f6df547 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -1216,6 +1216,9 @@ static void perf_event__header_size(struct perf_event *event)
> if (sample_type & PERF_SAMPLE_TRANSACTION)
> size += sizeof(data->txn);
>
> + if (sample_type & PERF_SAMPLE_CLOCK_RAW_MONOTONIC)
> + size += sizeof(data->clock_raw_monotonic);
> +
> event->header_size = size;
> }
>
> @@ -4456,6 +4459,13 @@ static void __perf_event_header__init_id(struct perf_event_header *header,
> data->cpu_entry.cpu = raw_smp_processor_id();
> data->cpu_entry.reserved = 0;
> }
> +
> + if (sample_type & PERF_SAMPLE_CLOCK_RAW_MONOTONIC) {
> + struct timespec now;
> +
> + getrawmonotonic(&now);
> + data->clock_raw_monotonic = timespec_to_ns(&now);
> + }
> }
>
> void perf_event_header__init_id(struct perf_event_header *header,
> @@ -4714,6 +4724,9 @@ void perf_output_sample(struct perf_output_handle *handle,
> if (sample_type & PERF_SAMPLE_TRANSACTION)
> perf_output_put(handle, data->txn);
>
> + if (sample_type & PERF_SAMPLE_CLOCK_RAW_MONOTONIC)
> + perf_output_put(handle, data->clock_raw_monotonic);
> +
> if (!event->attr.watermark) {
> int wakeup_events = event->attr.wakeup_events;
^ permalink raw reply
* [PATCH v5 3/3] dt-bindings: video: Add documentation for rockchip vop
From: Mark yao @ 2014-09-24 2:13 UTC (permalink / raw)
To: heiko, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api,
linux-rockchip, dianders, marcheu, dbehr, olof, djkurtz, xjq, kfx,
cym, cf, zyw, xxm, huangtao, kever.yang, yxj, wxt, xw, Mark yao
In-Reply-To: <1411524672-14524-1-git-send-email-mark.yao@rock-chips.com>
This adds binding documentation for Rockchip SoC VOP driver.
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
---
Changes in v2:
- rename "lcdc" to "vop"
- add vop reset
- add iommu node
- add port for display-subsystem
Changes in v3: None
Changes in v4: None
Changes in v5: None
.../devicetree/bindings/video/rockchip-vop.txt | 58 ++++++++++++++++++++
1 file changed, 58 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/rockchip-vop.txt
diff --git a/Documentation/devicetree/bindings/video/rockchip-vop.txt b/Documentation/devicetree/bindings/video/rockchip-vop.txt
new file mode 100644
index 0000000..d15351f
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-vop.txt
@@ -0,0 +1,58 @@
+device-tree bindings for rockchip soc display controller (vop)
+
+VOP (Visual Output Processor) 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,rk3288-vop";
+
+- interrupts: should contain a list of all VOP 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_vop: for ddr buffer transfer.
+ hclk_vop: for ahb bus to R/W the phy regs.
+ dclk_vop: pixel clock.
+
+- resets: Must contain an entry for each entry in reset-names.
+ See ../reset/reset.txt for details.
+- reset-names: Must include the following entries:
+ - axi
+ - ahb
+ - dclk
+
+- iommus: required a iommu node
+
+- port: A port node with endpoint definitions as defined in
+ Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+SoC specific DT entry:
+ vopb: vopb@ff930000 {
+ compatible = "rockchip,rk3288-vop";
+ reg = <0xff930000 0x19c>;
+ interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_VOP0>, <&cru DCLK_VOP0>, <&cru HCLK_VOP0>;
+ clock-names = "aclk_vop", "dclk_vop", "hclk_vop";
+ resets = <&cru SRST_LCDC1_AXI>, <&cru SRST_LCDC1_AHB>, <&cru SRST_LCDC1_DCLK>;
+ reset-names = "axi", "ahb", "dclk";
+ iommus = <&vopb_mmu>;
+ vopb_out: port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ vopb_out_edp: endpoint@0 {
+ reg = <0>;
+ remote-endpoint=<&edp_in_vopb>;
+ };
+ vopb_out_hdmi: endpoint@1 {
+ reg = <1>;
+ remote-endpoint=<&hdmi_in_vopb>;
+ };
+ };
+ };
--
1.7.9.5
^ permalink raw reply related
* [PATCH v5 2/3] dt-bindings: video: Add for rockchip display subsytem
From: Mark yao @ 2014-09-24 2:13 UTC (permalink / raw)
To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Boris BREZILLON, David Airlie,
Rob Clark, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
Kumar Gala, Randy Dunlap, Grant Likely, Greg Kroah-Hartman,
John Stultz, Rom Lemarchand
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
xxm-TNX95d0MmH7DzftRWevZcw, huangtao-TNX95d0MmH7DzftRWevZcw,
kever.yang-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, Mark yao
In-Reply-To: <1411524672-14524-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
This add a display subsystem comprise the all display interface nodes.
Signed-off-by: Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
Changes in v2:
- add DRM master device node to list all display nodes that comprise
the graphics subsystem.
Changes in v3: None
Changes in v4: None
Changes in v5: None
.../devicetree/bindings/video/rockchip-drm.txt | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/rockchip-drm.txt
diff --git a/Documentation/devicetree/bindings/video/rockchip-drm.txt b/Documentation/devicetree/bindings/video/rockchip-drm.txt
new file mode 100644
index 0000000..7fff582
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-drm.txt
@@ -0,0 +1,19 @@
+Rockchip DRM master device
+================================
+
+The Rockchip DRM master device is a virtual device needed to list all
+vop devices or other display interface nodes that comprise the
+graphics subsystem.
+
+Required properties:
+- compatible: Should be "rockchip,display-subsystem"
+- ports: Should contain a list of phandles pointing to display interface port
+ of vop devices. vop definitions as defined in
+ Documentation/devicetree/bindings/video/rockchip-vop.txt
+
+example:
+
+display-subsystem {
+ compatible = "rockchip,display-subsystem";
+ ports = <&vopl_out>, <&vopb_out>;
+};
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v5 1/3] drm/rockchip: Add basic drm driver
From: Mark yao @ 2014-09-24 2:12 UTC (permalink / raw)
To: heiko, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand
Cc: devicetree, linux-doc, linux-kernel, dri-devel, linux-api,
linux-rockchip, dianders, marcheu, dbehr, olof, djkurtz, xjq, kfx,
cym, cf, zyw, xxm, huangtao, kever.yang, yxj, wxt, xw, Mark yao
In-Reply-To: <1411524672-14524-1-git-send-email-mark.yao@rock-chips.com>
This patch adds the basic structure of a DRM Driver for Rockchip Socs.
Signed-off-by: Mark yao <mark.yao@rock-chips.com>
---
Changes in v2:
- use the component framework to defer main drm driver probe
until all VOP devices have been probed.
- use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
master device and each vop device can shared the drm dma mapping.
- use drm_crtc_init_with_planes and drm_universal_plane_init.
- remove unnecessary middle layers.
- add cursor set, move funcs to rockchip drm crtc.
- use vop reset at first init
- reference framebuffer when used and unreference when swap out vop
Changes in v3:
- change "crtc->fb" to "crtc->primary-fb"
Adviced by Daniel Vetter
- init cursor plane with universal api, remove unnecessary cursor set,move
Changes in v4:
Adviced by David Herrmann
- remove drm_platform_*() usage, use register drm device directly.
Adviced by Rob Clark
- remove special mmap ioctl, do userspace mmap with normal mmap() or mmap offset
Changes in v5:
Adviced by Arnd Bergmann
- doing DMA start with a 32-bit masks with dma_mask and dma_coherent_mark
- fix some incorrect dependencies.
Adviced by Boris BREZILLON
- fix some mistake and bugs.
Adviced by Daniel Vetter
- drop all special ioctl and use generic kms ioctl instead.
Adviced by Rob Clark
- use unlocked api for drm_fb_helper_restore_fbdev_mode.
- remove unused rockchip_gem_prime_import_sg_table.
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/rockchip/Kconfig | 17 +
drivers/gpu/drm/rockchip/Makefile | 8 +
drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 509 +++++++++
drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 120 +++
drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 201 ++++
drivers/gpu/drm/rockchip/rockchip_drm_fb.h | 28 +
drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 230 +++++
drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 20 +
drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 341 ++++++
drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 55 +
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 1373 +++++++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 187 ++++
14 files changed, 3092 insertions(+)
create mode 100644 drivers/gpu/drm/rockchip/Kconfig
create mode 100644 drivers/gpu/drm/rockchip/Makefile
create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.h
create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.c
create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.h
create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.c
create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.h
create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.c
create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index b066bb3..7c4c3c6 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -171,6 +171,8 @@ config DRM_SAVAGE
source "drivers/gpu/drm/exynos/Kconfig"
+source "drivers/gpu/drm/rockchip/Kconfig"
+
source "drivers/gpu/drm/vmwgfx/Kconfig"
source "drivers/gpu/drm/gma500/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 4a55d59..d03387a 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
+obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
obj-$(CONFIG_DRM_GMA500) += gma500/
obj-$(CONFIG_DRM_UDL) += udl/
obj-$(CONFIG_DRM_AST) += ast/
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
new file mode 100644
index 0000000..87255f7
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -0,0 +1,17 @@
+config DRM_ROCKCHIP
+ tristate "DRM Support for Rockchip"
+ depends on DRM && ROCKCHIP_IOMMU && ARM_DMA_USE_IOMMU && IOMMU_API
+ select DRM_KMS_HELPER
+ select DRM_KMS_FB_HELPER
+ select DRM_PANEL
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+ select VIDEOMODE_HELPERS
+ help
+ Choose this option if you have a Rockchip soc chipset.
+ This driver provides kernel mode setting and buffer
+ management to userspace. This driver does not provides
+ 2D or 3D acceleration; acceleration is performed by other
+ IP found on the SoC.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
new file mode 100644
index 0000000..b3a5193
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the drm device driver. This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
+ rockchip_drm_gem.o rockchip_drm_vop.o
+
+obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
new file mode 100644
index 0000000..0ca4a6b
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_drv.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/dma-iommu.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_graph.h>
+#include <linux/component.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_gem.h"
+
+#define DRIVER_NAME "rockchip"
+#define DRIVER_DESC "RockChip Soc DRM"
+#define DRIVER_DATE "20140818"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+/*
+ * Attach a (component) device to the shared drm dma mapping from master drm
+ * device. This is used by the VOPs to map GEM buffers to a common DMA
+ * mapping.
+ */
+int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
+ struct device *dev)
+{
+ struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
+ int ret;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ dma_set_max_seg_size(dev, 0xffffffffu);
+
+ return arm_iommu_attach_device(dev, mapping);
+}
+
+void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
+ struct device *dev)
+{
+ arm_iommu_detach_device(drm_dev->dev);
+}
+
+static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
+{
+ struct rockchip_drm_private *private;
+ struct dma_iommu_mapping *mapping;
+ struct device *dev = drm_dev->dev;
+ int ret;
+
+ private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
+ if (!private)
+ return -ENOMEM;
+
+ drm_dev->dev_private = private;
+
+ drm_mode_config_init(drm_dev);
+
+ rockchip_drm_mode_config_init(drm_dev);
+
+ dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
+ GFP_KERNEL);
+ if (!dev->dma_parms) {
+ ret = -ENOMEM;
+ goto err_config_cleanup;
+ }
+
+ /* TODO(djkurtz): fetch the mapping start/size from somewhere */
+ mapping = arm_iommu_create_mapping(&platform_bus_type, 0x10000000,
+ SZ_1G);
+ if (IS_ERR(mapping)) {
+ ret = PTR_ERR(mapping);
+ goto err_config_cleanup;
+ }
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ goto err_config_cleanup;
+
+ dma_set_max_seg_size(dev, 0xffffffffu);
+
+ ret = arm_iommu_attach_device(dev, mapping);
+ if (ret)
+ goto err_release_mapping;
+
+ /* Try to bind all sub drivers. */
+ ret = component_bind_all(dev, drm_dev);
+ if (ret)
+ goto err_detach_device;
+
+ /* init kms poll for handling hpd */
+ drm_kms_helper_poll_init(drm_dev);
+
+ /*
+ * enable drm irq mode.
+ * - with irq_enabled = true, we can use the vblank feature.
+ */
+ drm_dev->irq_enabled = true;
+
+ /*
+ * with vblank_disable_allowed = true, vblank interrupt will be disabled
+ * by drm timer once a current process gives up ownership of
+ * vblank event.(after drm_vblank_put function is called)
+ */
+ drm_dev->vblank_disable_allowed = true;
+
+ ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC);
+ if (ret)
+ goto err_kms_helper_poll_fini;
+
+ rockchip_drm_fbdev_init(drm_dev);
+
+ /* force connectors detection */
+ drm_helper_hpd_irq_event(drm_dev);
+
+ return 0;
+
+err_kms_helper_poll_fini:
+ drm_kms_helper_poll_fini(drm_dev);
+ component_unbind_all(dev, drm_dev);
+err_detach_device:
+ arm_iommu_detach_device(dev);
+err_release_mapping:
+ arm_iommu_release_mapping(dev->archdata.mapping);
+err_config_cleanup:
+ drm_mode_config_cleanup(drm_dev);
+ drm_dev->dev_private = NULL;
+ return ret;
+}
+
+static int rockchip_drm_unload(struct drm_device *drm_dev)
+{
+ struct device *dev = drm_dev->dev;
+
+ drm_kms_helper_poll_fini(drm_dev);
+ component_unbind_all(dev, drm_dev);
+ arm_iommu_detach_device(dev);
+ arm_iommu_release_mapping(dev->archdata.mapping);
+ drm_mode_config_cleanup(drm_dev);
+ drm_dev->dev_private = NULL;
+
+ return 0;
+}
+
+static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state)
+{
+ struct drm_connector *connector;
+
+ drm_modeset_lock_all(dev);
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ int old_dpms = connector->dpms;
+
+ if (connector->funcs->dpms)
+ connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
+
+ /* Set the old mode back to the connector for resume */
+ connector->dpms = old_dpms;
+ }
+ drm_modeset_unlock_all(dev);
+
+ return 0;
+}
+
+static int rockchip_drm_resume(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+
+ drm_modeset_lock_all(dev);
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->funcs->dpms)
+ connector->funcs->dpms(connector, connector->dpms);
+ }
+ drm_modeset_unlock_all(dev);
+
+ drm_helper_resume_force_mode(dev);
+
+ return 0;
+}
+
+void rockchip_drm_lastclose(struct drm_device *dev)
+{
+ struct rockchip_drm_private *priv = dev->dev_private;
+
+ if (priv->fb_helper)
+ drm_fb_helper_restore_fbdev_mode_unlocked(priv->fb_helper);
+}
+
+static const struct file_operations rockchip_drm_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = rockchip_drm_gem_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .release = drm_release,
+};
+
+const struct vm_operations_struct rockchip_drm_vm_ops = {
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static struct drm_driver rockchip_drm_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+ .load = rockchip_drm_load,
+ .unload = rockchip_drm_unload,
+ .lastclose = rockchip_drm_lastclose,
+ .suspend = rockchip_drm_suspend,
+ .resume = rockchip_drm_resume,
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = rockchip_drm_crtc_enable_vblank,
+ .disable_vblank = rockchip_drm_crtc_disable_vblank,
+ .gem_vm_ops = &rockchip_drm_vm_ops,
+ .gem_free_object = rockchip_gem_free_object,
+ .dumb_create = rockchip_gem_dumb_create,
+ .dumb_map_offset = rockchip_gem_dumb_map_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
+ .gem_prime_vmap = rockchip_gem_prime_vmap,
+ .gem_prime_vunmap = rockchip_gem_prime_vunmap,
+ .gem_prime_mmap = rockchip_gem_prime_mmap,
+ .ioctls = rockchip_ioctls,
+ .num_ioctls = ARRAY_SIZE(rockchip_ioctls),
+ .fops = &rockchip_drm_driver_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_drm_sys_suspend(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+ pm_message_t message;
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ message.event = PM_EVENT_SUSPEND;
+
+ return rockchip_drm_suspend(drm_dev, message);
+}
+
+static int rockchip_drm_sys_resume(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ return rockchip_drm_resume(drm_dev);
+}
+#endif
+
+static const struct dev_pm_ops rockchip_drm_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
+ rockchip_drm_sys_resume)
+};
+
+int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
+ struct device_node *np)
+{
+ struct rockchip_drm_private *priv = drm->dev_private;
+ struct device_node *port;
+ int pipe;
+
+ if (priv->num_pipe >= ROCKCHIP_MAX_CRTC)
+ return -EINVAL;
+
+ port = of_get_child_by_name(np, "port");
+ if (!port) {
+ dev_err(drm->dev, "no port node found in %s\n",
+ np->full_name);
+ return -ENXIO;
+ }
+ pipe = priv->num_pipe++;
+ crtc->port = port;
+
+ priv->crtc[pipe] = crtc;
+
+ return pipe;
+}
+
+void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe)
+{
+ struct rockchip_drm_private *priv = drm->dev_private;
+
+ priv->num_pipe--;
+ of_node_put(priv->crtc[pipe]->port);
+ priv->crtc[pipe] = NULL;
+}
+
+struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe)
+{
+ struct rockchip_drm_private *priv = drm->dev_private;
+
+ if (pipe < ROCKCHIP_MAX_CRTC && priv->crtc[pipe])
+ return priv->crtc[pipe];
+
+ return NULL;
+}
+
+/*
+ * @node: device tree node containing encoder input ports
+ * @encoder: drm_encoder
+ */
+int rockchip_drm_encoder_get_mux_id(struct device_node *node,
+ struct drm_encoder *encoder)
+{
+ struct device_node *ep = NULL;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct of_endpoint endpoint;
+ struct device_node *port;
+ int ret;
+
+ if (!node || !crtc)
+ return -EINVAL;
+
+ do {
+ ep = of_graph_get_next_endpoint(node, ep);
+ if (!ep)
+ break;
+
+ port = of_graph_get_remote_port(ep);
+ of_node_put(port);
+ if (port == crtc->port) {
+ ret = of_graph_parse_endpoint(ep, &endpoint);
+ return ret ? ret : endpoint.id;
+ }
+ } while (ep);
+
+ return -EINVAL;
+}
+
+static int compare_of(struct device *dev, void *data)
+{
+ struct device_node *np = data;
+
+ return dev->of_node == np;
+}
+
+static void rockchip_add_endpoints(struct device *dev,
+ struct component_match **match,
+ struct device_node *port)
+{
+ struct device_node *ep, *remote;
+
+ for_each_child_of_node(port, ep) {
+ remote = of_graph_get_remote_port_parent(ep);
+ if (!remote || !of_device_is_available(remote)) {
+ of_node_put(remote);
+ continue;
+ } else if (!of_device_is_available(remote->parent)) {
+ dev_warn(dev, "parent device of %s is not available\n",
+ remote->full_name);
+ of_node_put(remote);
+ continue;
+ }
+
+ component_match_add(dev, match, compare_of, remote);
+ of_node_put(remote);
+ }
+}
+
+static int rockchip_drm_bind(struct device *dev)
+{
+ struct drm_device *drm;
+ int ret;
+
+ drm = drm_dev_alloc(&rockchip_drm_driver, dev);
+ if (!drm)
+ return -ENOMEM;
+
+ ret = drm_dev_set_unique(drm, "%s", dev_name(dev));
+ if (ret)
+ goto err_free;
+
+ ret = drm_dev_register(drm, 0);
+ if (ret)
+ goto err_free;
+
+ dev_set_drvdata(dev, drm);
+
+ return 0;
+
+err_free:
+ drm_dev_unref(drm);
+ return ret;
+}
+
+static void rockchip_drm_unbind(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+
+ drm_dev_unregister(drm);
+ drm_dev_unref(drm);
+}
+
+static const struct component_master_ops rockchip_drm_ops = {
+ .bind = rockchip_drm_bind,
+ .unbind = rockchip_drm_unbind,
+};
+
+static int rockchip_drm_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct component_match *match = NULL;
+ struct device_node *np = dev->of_node;
+ struct device_node *port;
+ int i;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+ /*
+ * Bind the crtc ports first, so that
+ * drm_of_find_possible_crtcs called from encoder .bind callbacks
+ * works as expected.
+ */
+ for (i = 0;; i++) {
+ port = of_parse_phandle(np, "ports", i);
+ if (!port)
+ break;
+
+ component_match_add(dev, &match, compare_of, port->parent);
+ of_node_put(port);
+ }
+
+ if (i == 0) {
+ dev_err(dev, "missing 'ports' property\n");
+ return -ENODEV;
+ }
+ /*
+ * For each bound crtc, bind the encoders attached to its
+ * remote endpoint.
+ */
+ for (i = 0;; i++) {
+ port = of_parse_phandle(np, "ports", i);
+ if (!port)
+ break;
+
+ rockchip_add_endpoints(dev, &match, port);
+ of_node_put(port);
+ }
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ return component_master_add_with_match(dev, &rockchip_drm_ops, match);
+}
+
+static int rockchip_drm_platform_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &rockchip_drm_ops);
+
+ return 0;
+}
+
+static const struct of_device_id rockchip_drm_dt_ids[] = {
+ { .compatible = "rockchip,display-subsystem", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
+
+static struct platform_driver rockchip_drm_platform_driver = {
+ .probe = rockchip_drm_platform_probe,
+ .remove = rockchip_drm_platform_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rockchip-drm",
+ .of_match_table = rockchip_drm_dt_ids,
+ },
+};
+
+module_platform_driver(rockchip_drm_platform_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
new file mode 100644
index 0000000..154b3ec
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_drv.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_DRV_H
+#define _ROCKCHIP_DRM_DRV_H
+
+#include <linux/module.h>
+#include <linux/component.h>
+
+#define ROCKCHIP_MAX_FB_BUFFER 4
+#define ROCKCHIP_MAX_CONNECTOR 2
+
+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 mode info for rockchip
+ *
+ * @out_type: lcd controller need to know the sceen type.
+ */
+struct rockchip_display_mode {
+ int out_type;
+};
+
+#define ROCKCHIP_EVENT_HOTPLUG 1
+
+enum rockchip_plane_type {
+ ROCKCHIP_WIN0,
+ ROCKCHIP_WIN1,
+ ROCKCHIP_WIN2,
+ ROCKCHIP_WIN3,
+ ROCKCHIP_CURSOR,
+ ROCKCHIP_MAX_PLANE,
+};
+
+/* This enumerates device type. */
+enum rockchip_drm_device_type {
+ ROCKCHIP_DEVICE_TYPE_NONE,
+ ROCKCHIP_DEVICE_TYPE_CRTC,
+ ROCKCHIP_DEVICE_TYPE_CONNECTOR,
+};
+
+/* this enumerates display type. */
+enum rockchip_drm_output_type {
+ ROCKCHIP_DISPLAY_TYPE_NONE = 0,
+ /* RGB Interface. */
+ ROCKCHIP_DISPLAY_TYPE_RGB,
+ /* LVDS Interface. */
+ ROCKCHIP_DISPLAY_TYPE_LVDS,
+ /* EDP Interface. */
+ ROCKCHIP_DISPLAY_TYPE_EDP,
+ /* MIPI Interface. */
+ ROCKCHIP_DISPLAY_TYPE_MIPI,
+ /* HDMI Interface. */
+ ROCKCHIP_DISPLAY_TYPE_HDMI,
+};
+
+enum rockchip_crtc_type {
+ ROCKCHIP_CRTC_VOPB,
+ ROCKCHIP_CRTC_VOPL,
+ ROCKCHIP_MAX_CRTC,
+};
+
+/*
+ * Rockchip drm private structure.
+ *
+ * @num_pipe: number of pipes for this device.
+ */
+struct rockchip_drm_private {
+ struct drm_fb_helper *fb_helper;
+ /*
+ * created crtc object would be contained at this array and
+ * this array is used to be aware of which crtc did it request vblank.
+ */
+ struct drm_crtc *crtc[ROCKCHIP_MAX_CRTC];
+
+ unsigned int num_pipe;
+};
+
+int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
+ struct device_node *port);
+void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe);
+struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe);
+int rockchip_drm_encoder_get_mux_id(struct device_node *node,
+ struct drm_encoder *encoder);
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
+void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev);
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
+int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
+ struct device *dev);
+void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
+ struct device *dev);
+#endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
new file mode 100644
index 0000000..482f7b8
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
+
+struct rockchip_drm_fb {
+ struct drm_framebuffer fb;
+ struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
+};
+
+struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
+ unsigned int plane)
+{
+ struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
+
+ if (plane >= ROCKCHIP_MAX_FB_BUFFER)
+ return NULL;
+
+ return rk_fb->obj[plane];
+}
+
+static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+ struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+ struct drm_gem_object *obj;
+ int i;
+
+ for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
+ obj = rockchip_fb->obj[i];
+ if (obj)
+ drm_gem_object_unreference_unlocked(obj);
+ }
+
+ drm_framebuffer_cleanup(fb);
+ kfree(rockchip_fb);
+}
+
+static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int *handle)
+{
+ struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+
+ return drm_gem_handle_create(file_priv,
+ rockchip_fb->obj[0], handle);
+}
+
+static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
+ .destroy = rockchip_drm_fb_destroy,
+ .create_handle = rockchip_drm_fb_create_handle,
+};
+
+static struct rockchip_drm_fb *
+rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object **obj, unsigned int num_planes)
+{
+ struct rockchip_drm_fb *rockchip_fb;
+ int ret;
+ int i;
+
+ rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
+ if (!rockchip_fb)
+ return ERR_PTR(-ENOMEM);
+
+ drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
+
+ for (i = 0; i < num_planes; i++)
+ rockchip_fb->obj[i] = obj[i];
+
+ ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
+ &rockchip_drm_fb_funcs);
+ if (ret) {
+ dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
+ ret);
+ kfree(rockchip_fb);
+ return ERR_PTR(ret);
+ }
+
+ return rockchip_fb;
+}
+
+static struct drm_framebuffer *
+rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+ struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct rockchip_drm_fb *rockchip_fb;
+ struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
+ struct drm_gem_object *obj;
+ unsigned int hsub;
+ unsigned int vsub;
+ int num_planes;
+ int ret;
+ int i;
+
+ hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
+ vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
+ num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
+ ROCKCHIP_MAX_FB_BUFFER);
+
+ for (i = 0; i < num_planes; i++) {
+ unsigned int width = mode_cmd->width / (i ? hsub : 1);
+ unsigned int height = mode_cmd->height / (i ? vsub : 1);
+ unsigned int min_size;
+
+ obj = drm_gem_object_lookup(dev, file_priv,
+ mode_cmd->handles[i]);
+ if (!obj) {
+ dev_err(dev->dev, "Failed to lookup GEM object\n");
+ ret = -ENXIO;
+ goto err_gem_object_unreference;
+ }
+
+ min_size = (height - 1) * mode_cmd->pitches[i] +
+ mode_cmd->offsets[i] +
+ width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
+
+ if (obj->size < min_size) {
+ drm_gem_object_unreference_unlocked(obj);
+ ret = -EINVAL;
+ goto err_gem_object_unreference;
+ }
+ objs[i] = obj;
+ }
+
+ rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
+ if (IS_ERR(rockchip_fb)) {
+ ret = PTR_ERR(rockchip_fb);
+ goto err_gem_object_unreference;
+ }
+
+ return &rockchip_fb->fb;
+
+err_gem_object_unreference:
+ for (i--; i >= 0; i--)
+ drm_gem_object_unreference_unlocked(objs[i]);
+ return ERR_PTR(ret);
+}
+
+static void rockchip_drm_output_poll_changed(struct drm_device *dev)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct drm_fb_helper *fb_helper = private->fb_helper;
+
+ if (fb_helper)
+ drm_fb_helper_hotplug_event(fb_helper);
+}
+
+static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
+ .fb_create = rockchip_user_fb_create,
+ .output_poll_changed = rockchip_drm_output_poll_changed,
+};
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj)
+{
+ struct rockchip_drm_fb *rockchip_fb;
+
+ rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
+ if (IS_ERR(rockchip_fb))
+ return NULL;
+
+ return &rockchip_fb->fb;
+}
+
+void rockchip_drm_mode_config_init(struct drm_device *dev)
+{
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+
+ /*
+ * set max width and height as default value(4096x4096).
+ * this value would be used to check framebuffer size limitation
+ * at drm_mode_addfb().
+ */
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+
+ dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
new file mode 100644
index 0000000..09574d4
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_FB_H
+#define _ROCKCHIP_DRM_FB_H
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj);
+void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb);
+
+void rockchip_drm_mode_config_init(struct drm_device *dev);
+
+struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
+ unsigned int plane);
+#endif /* _ROCKCHIP_DRM_FB_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
new file mode 100644
index 0000000..ae563f5
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_fb.h"
+
+#define PREFERRED_BPP 32
+#define to_rockchip_fbdev(x) container_of(x, struct rockchip_fbdev, helper)
+
+struct rockchip_fbdev {
+ struct drm_fb_helper helper;
+ struct drm_gem_object *bo;
+};
+
+static int rockchip_fbdev_mmap(struct fb_info *info,
+ struct vm_area_struct *vma)
+{
+ struct drm_fb_helper *helper = info->par;
+ struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
+
+ return rockchip_gem_mmap(fbdev->bo, vma);
+}
+
+static struct fb_ops rockchip_drm_fbdev_ops = {
+ .owner = THIS_MODULE,
+ .fb_mmap = rockchip_fbdev_mmap,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = drm_fb_helper_set_par,
+ .fb_blank = drm_fb_helper_blank,
+ .fb_pan_display = drm_fb_helper_pan_display,
+ .fb_setcmap = drm_fb_helper_setcmap,
+};
+
+static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
+ struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+ struct drm_device *dev = helper->dev;
+ struct rockchip_gem_object *rk_obj;
+ struct drm_framebuffer *fb;
+ unsigned int bytes_per_pixel;
+ unsigned long offset;
+ struct fb_info *fbi;
+ size_t size;
+ int ret;
+
+ bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+
+ size = mode_cmd.pitches[0] * mode_cmd.height;
+
+ rk_obj = rockchip_gem_create_object(dev, size);
+ if (IS_ERR(rk_obj))
+ return -ENOMEM;
+
+ fbdev->bo = &rk_obj->base;
+
+ fbi = framebuffer_alloc(0, dev->dev);
+ if (!fbi) {
+ dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
+ ret = -ENOMEM;
+ goto err_rockchip_gem_free_object;
+ }
+
+ helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd, fbdev->bo);
+ if (IS_ERR(helper->fb)) {
+ dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
+ ret = PTR_ERR(helper->fb);
+ goto err_framebuffer_release;
+ }
+
+ helper->fbdev = fbi;
+
+ fbi->par = helper;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->fbops = &rockchip_drm_fbdev_ops;
+
+ ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+ if (ret) {
+ dev_err(dev->dev, "Failed to allocate color map.\n");
+ goto err_drm_framebuffer_unref;
+ }
+
+ fb = helper->fb;
+ drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
+ drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
+
+ offset = fbi->var.xoffset * bytes_per_pixel;
+ offset += fbi->var.yoffset * fb->pitches[0];
+
+ dev->mode_config.fb_base = 0;
+ fbi->screen_base = rk_obj->kvaddr + offset;
+ fbi->screen_size = rk_obj->base.size;
+ fbi->fix.smem_len = rk_obj->base.size;
+
+ DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n",
+ fb->width, fb->height, fb->depth, rk_obj->kvaddr,
+ offset, size);
+ return 0;
+
+err_drm_framebuffer_unref:
+ drm_framebuffer_unreference(helper->fb);
+err_framebuffer_release:
+ framebuffer_release(fbi);
+err_rockchip_gem_free_object:
+ rockchip_gem_free_object(&rk_obj->base);
+ return ret;
+}
+
+static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
+ .fb_probe = rockchip_drm_fbdev_create,
+};
+
+int rockchip_drm_fbdev_init(struct drm_device *dev)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct rockchip_fbdev *fbdev;
+ struct drm_fb_helper *helper;
+ unsigned int num_crtc;
+ int ret;
+
+ if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
+ return -EINVAL;
+
+ if (private->fb_helper) {
+ DRM_ERROR("no allow to reinit fbdev\n");
+ return -EINVAL;
+ }
+
+ num_crtc = dev->mode_config.num_crtc;
+
+ fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
+ if (!fbdev)
+ return -ENOMEM;
+
+ fbdev->helper.funcs = &rockchip_drm_fb_helper_funcs;
+ helper = &fbdev->helper;
+
+ ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
+ goto err_free;
+ }
+
+ ret = drm_fb_helper_single_add_all_connectors(helper);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to add connectors.\n");
+ goto err_drm_fb_helper_fini;
+ }
+
+ /* disable all the possible outputs/crtcs before entering KMS mode */
+ drm_helper_disable_unused_functions(dev);
+
+ ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to set initial hw configuration.\n");
+ goto err_drm_fb_helper_fini;
+ }
+
+ private->fb_helper = helper;
+
+ return 0;
+
+err_drm_fb_helper_fini:
+ drm_fb_helper_fini(helper);
+err_free:
+ kfree(fbdev);
+ return ret;
+}
+
+void rockchip_drm_fbdev_fini(struct drm_device *dev)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct drm_fb_helper *helper;
+ struct rockchip_fbdev *fbdev;
+
+ if (!private || !private->fb_helper)
+ return;
+
+ helper = private->fb_helper;
+ fbdev = to_rockchip_fbdev(helper);
+
+ if (helper->fbdev) {
+ struct fb_info *info;
+ int ret;
+
+ info = helper->fbdev;
+ ret = unregister_framebuffer(info);
+ if (ret < 0)
+ DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
+
+ if (info->cmap.len)
+ fb_dealloc_cmap(&info->cmap);
+
+ framebuffer_release(info);
+ }
+
+ if (helper->fb)
+ drm_framebuffer_unreference(helper->fb);
+
+ drm_fb_helper_fini(helper);
+ kfree(fbdev);
+ private->fb_helper = NULL;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
new file mode 100644
index 0000000..5edcf6a
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_FBDEV_H
+#define _ROCKCHIP_DRM_FBDEV_H
+
+int rockchip_drm_fbdev_init(struct drm_device *dev);
+
+#endif /* _ROCKCHIP_DRM_FBDEV_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
new file mode 100644
index 0000000..8e7a91c
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
+
+#include <linux/anon_inodes.h>
+#include <linux/dma-attrs.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj)
+{
+ struct drm_gem_object *obj = &rk_obj->base;
+ struct drm_device *drm = obj->dev;
+
+ init_dma_attrs(&rk_obj->dma_attrs);
+ dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
+
+ /* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */
+ rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
+ &rk_obj->dma_addr, GFP_KERNEL,
+ &rk_obj->dma_attrs);
+ if (IS_ERR(rk_obj->kvaddr)) {
+ int ret = PTR_ERR(rk_obj->kvaddr);
+
+ DRM_ERROR("failed to allocate %#x byte dma buffer, %d",
+ obj->size, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
+{
+ struct drm_gem_object *obj = &rk_obj->base;
+ struct drm_device *drm = obj->dev;
+
+ dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
+ &rk_obj->dma_attrs);
+}
+
+/* drm driver mmap file operations */
+int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->minor->dev;
+ struct drm_gem_object *obj;
+ struct drm_vma_offset_node *node;
+ int ret;
+
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
+ mutex_lock(&dev->struct_mutex);
+
+ node = drm_vma_offset_exact_lookup(dev->vma_offset_manager,
+ vma->vm_pgoff,
+ vma_pages(vma));
+ if (!node) {
+ mutex_unlock(&dev->struct_mutex);
+ DRM_ERROR("failed to find vma node.\n");
+ return -EINVAL;
+ } else if (!drm_vma_node_is_allowed(node, filp)) {
+ mutex_unlock(&dev->struct_mutex);
+ return -EACCES;
+ }
+
+ obj = container_of(node, struct drm_gem_object, vma_node);
+ ret = rockchip_gem_mmap(obj, vma);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
+}
+
+int rockchip_drm_gem_mmap_buffer(struct file *filp,
+ struct vm_area_struct *vma)
+{
+ struct drm_gem_object *obj = filp->private_data;
+
+ return rockchip_gem_mmap(obj, vma);
+}
+
+static const struct file_operations rockchip_drm_gem_fops = {
+ .mmap = rockchip_drm_gem_mmap_buffer,
+};
+
+struct rockchip_gem_object *
+ rockchip_gem_create_object(struct drm_device *drm, unsigned int size)
+{
+ struct rockchip_gem_object *rk_obj;
+ struct drm_gem_object *obj;
+ struct file *filp;
+ int ret;
+
+ size = round_up(size, PAGE_SIZE);
+
+ rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
+ if (!rk_obj)
+ return ERR_PTR(-ENOMEM);
+
+ obj = &rk_obj->base;
+
+ drm_gem_private_object_init(drm, obj, size);
+
+ filp = anon_inode_getfile("rockchip_gem", &rockchip_drm_gem_fops,
+ obj, 0);
+ if (IS_ERR(filp)) {
+ DRM_ERROR("failed to create anon file object.\n");
+ ret = PTR_ERR(filp);
+ goto err_free_rk_obj;
+ }
+ filp->f_mode = FMODE_READ | FMODE_WRITE;
+ obj->filp = filp;
+
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret)
+ goto err_free_obj;
+
+ ret = rockchip_gem_alloc_buf(rk_obj);
+ if (ret)
+ goto err_free_mmap_offset;
+
+ return rk_obj;
+
+err_free_mmap_offset:
+ drm_gem_free_mmap_offset(obj);
+err_free_obj:
+ drm_gem_object_release(obj);
+err_free_rk_obj:
+ kfree(rk_obj);
+ return ERR_PTR(ret);
+}
+
+/*
+ * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback
+ * function
+ */
+void rockchip_gem_free_object(struct drm_gem_object *obj)
+{
+ struct rockchip_gem_object *rk_obj;
+
+ drm_gem_free_mmap_offset(obj);
+
+ rk_obj = to_rockchip_obj(obj);
+
+ rockchip_gem_free_buf(rk_obj);
+ drm_gem_free_mmap_offset(obj);
+
+ drm_gem_object_release(obj);
+
+ kfree(rk_obj);
+}
+
+int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+ struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+ struct drm_device *drm = obj->dev;
+ unsigned long vm_size;
+
+ vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
+ vm_size = vma->vm_end - vma->vm_start;
+
+ if (vm_size > obj->size)
+ return -EINVAL;
+
+ return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
+ obj->size, &rk_obj->dma_attrs);
+}
+
+/*
+ * rockchip_gem_create_with_handle - allocate an object with the given
+ * size and create a gem handle on it
+ *
+ * returns a struct rockchip_gem_object* on success or ERR_PTR values
+ * on failure.
+ */
+static struct rockchip_gem_object *
+rockchip_gem_create_with_handle(struct drm_file *file_priv,
+ struct drm_device *drm, unsigned int size,
+ unsigned int *handle)
+{
+ struct rockchip_gem_object *rk_obj;
+ struct drm_gem_object *obj;
+ int ret;
+
+ rk_obj = rockchip_gem_create_object(drm, size);
+ if (IS_ERR(rk_obj))
+ return NULL;
+
+ obj = &rk_obj->base;
+
+ /*
+ * allocate a id of idr table where the obj is registered
+ * and handle has the id what user can see.
+ */
+ ret = drm_gem_handle_create(file_priv, obj, handle);
+ if (ret)
+ goto err_handle_create;
+
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_unreference_unlocked(obj);
+
+ return rk_obj;
+
+err_handle_create:
+ rockchip_gem_free_object(obj);
+
+ return ERR_PTR(ret);
+}
+
+int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *dev, uint32_t handle,
+ uint64_t *offset)
+{
+ struct drm_gem_object *obj;
+ int ret = 0;
+
+ mutex_lock(&dev->struct_mutex);
+
+ /*
+ * get offset of memory allocated for drm framebuffer.
+ * - this callback would be called by user application
+ * with DRM_IOCTL_MODE_MAP_DUMB command.
+ */
+
+ obj = drm_gem_object_lookup(dev, file_priv, handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret)
+ goto out;
+
+ *offset = drm_vma_node_offset_addr(&obj->vma_node);
+ DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
+
+out:
+ drm_gem_object_unreference(obj);
+unlock:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+/*
+ * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
+ * function
+ *
+ * This aligns the pitch and size arguments to the minimum required. wrap
+ * this into your own function if you need bigger alignment.
+ */
+int rockchip_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ struct rockchip_gem_object *rk_obj;
+ int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+ if (args->pitch < min_pitch)
+ args->pitch = min_pitch;
+
+ if (args->size < args->pitch * args->height)
+ args->size = args->pitch * args->height;
+
+ rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
+ &args->handle);
+
+ return PTR_ERR_OR_ZERO(rk_obj);
+}
+
+/*
+ * Allocate a sg_table for this GEM object.
+ * Note: Both the table's contents, and the sg_table itself must be freed by
+ * the caller.
+ * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
+ */
+struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+ struct drm_device *drm = obj->dev;
+ struct sg_table *sgt = NULL;
+ int ret;
+
+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt)
+ return ERR_PTR(-ENOMEM);
+
+ ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
+ rk_obj->dma_addr, obj->size,
+ &rk_obj->dma_attrs);
+ if (ret) {
+ DRM_ERROR("failed to allocate sgt, %d\n", ret);
+ kfree(sgt);
+ return ERR_PTR(ret);
+ }
+
+ return sgt;
+}
+
+void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
+{
+ struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+
+ return rk_obj->kvaddr;
+}
+
+void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+ /* Nothing to do */
+}
+
+int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
+ struct vm_area_struct *vma)
+{
+ struct drm_device *dev = obj->dev;
+ int ret;
+
+ mutex_lock(&dev->struct_mutex);
+ ret = drm_gem_mmap_obj(obj, obj->size, vma);
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
new file mode 100644
index 0000000..e37b921
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_GEM_H
+#define _ROCKCHIP_DRM_GEM_H
+
+#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base)
+
+struct rockchip_gem_object {
+ struct drm_gem_object base;
+ unsigned int flags;
+
+ void *kvaddr;
+ dma_addr_t dma_addr;
+ struct dma_attrs dma_attrs;
+};
+
+struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *
+rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
+ struct sg_table *sgt);
+void *rockchip_gem_prime_vmap(struct drm_gem_object *obj);
+void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
+ struct vm_area_struct *vma);
+
+/* drm driver mmap file operations */
+int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+
+/* mmap a gem object to userspace. */
+int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
+
+struct rockchip_gem_object *
+ rockchip_gem_create_object(struct drm_device *drm, unsigned int size);
+
+void rockchip_gem_free_object(struct drm_gem_object *obj);
+
+int rockchip_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *dev, uint32_t handle,
+ uint64_t *offset);
+#endif /* _ROCKCHIP_DRM_GEM_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
new file mode 100644
index 0000000..24c3946
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -0,0 +1,1373 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/component.h>
+
+#include <linux/reset.h>
+#include <linux/iommu.h>
+#include <linux/delay.h>
+
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_vop.h"
+
+#define VOP_DEFAULT_FRAMERATE 60
+#define VOP_MAX_WIN_SUPPORT 5
+#define VOP_DEFAULT_CURSOR 1
+#define VOP_REG(off, _mask, s) \
+ {.offset = off, \
+ .mask = _mask, \
+ .shift = s,}
+
+#define __REG_SET(x, off, mask, shift, v) \
+ vop_mask_write(x, off, (mask) << shift, (v) << shift)
+
+#define REG_SET(x, base, reg, v) \
+ __REG_SET(x, base + reg.offset, reg.mask, reg.shift, v)
+
+#define VOP_WIN_SET(x, win, name, v) \
+ REG_SET(x, win->base, win->phy->name, v)
+#define VOP_CTRL_SET(x, name, v) \
+ REG_SET(x, 0, (x)->data->ctrl->name, v)
+
+#define VOP_WIN_GET_YRGBADDR(ctx, win) \
+ vop_readl(ctx, win->base + win->phy->yrgb_mst.offset)
+
+#define to_vop_ctx(x) container_of(x, struct vop_context, crtc)
+#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
+
+struct rockchip_plane {
+ int id;
+ struct drm_plane base;
+ const struct vop_win *win;
+ struct vop_context *ctx;
+
+ uint32_t pending_yrgb_mst;
+ struct drm_framebuffer *front_fb;
+ struct drm_framebuffer *pending_fb;
+ bool enabled;
+};
+
+struct vop_context {
+ struct device *dev;
+ struct drm_device *drm_dev;
+ struct drm_crtc crtc;
+ struct drm_pending_vblank_event *event;
+ struct vop_driver *drv;
+ unsigned int dpms;
+ unsigned int win_mask;
+ wait_queue_head_t wait_vsync_queue;
+ atomic_t wait_vsync_event;
+
+ struct workqueue_struct *vsync_wq;
+ struct work_struct vsync_work;
+
+ /* mutex vsync_ work */
+ struct mutex vsync_mutex;
+ bool vsync_work_pending;
+
+ struct vop_driver_data *data;
+
+ uint32_t *regsbak;
+ void __iomem *regs;
+
+ /* physical map length of vop register */
+ uint32_t len;
+
+ /* one time only one process allowed to config the register */
+ spinlock_t reg_lock;
+ /* lock vop irq reg */
+ spinlock_t irq_lock;
+
+ unsigned int irq;
+
+ /* vop AHP clk */
+ struct clk *hclk;
+ /* vop dclk */
+ struct clk *dclk;
+ /* vop share memory frequency */
+ struct clk *aclk;
+ uint32_t pixclock;
+
+ int pipe;
+ bool clk_on;
+};
+
+enum vop_data_format {
+ VOP_FMT_ARGB8888 = 0,
+ VOP_FMT_RGB888,
+ VOP_FMT_RGB565,
+ VOP_FMT_YUV420SP = 4,
+ VOP_FMT_YUV422SP,
+ VOP_FMT_YUV444SP,
+};
+
+struct vop_reg_data {
+ uint32_t offset;
+ uint32_t value;
+};
+
+struct vop_reg {
+ uint32_t offset;
+ uint32_t shift;
+ uint32_t mask;
+};
+
+struct vop_ctrl {
+ struct vop_reg standby;
+ struct vop_reg gate_en;
+ struct vop_reg mmu_en;
+ struct vop_reg rgb_en;
+ struct vop_reg edp_en;
+ struct vop_reg hdmi_en;
+ struct vop_reg mipi_en;
+ struct vop_reg out_mode;
+ struct vop_reg dither_down;
+ struct vop_reg dither_up;
+ struct vop_reg pin_pol;
+
+ struct vop_reg htotal_pw;
+ struct vop_reg hact_st_end;
+ struct vop_reg vtotal_pw;
+ struct vop_reg vact_st_end;
+ struct vop_reg hpost_st_end;
+ struct vop_reg vpost_st_end;
+};
+
+struct vop_win_phy {
+ const uint32_t *data_formats;
+ uint32_t nformats;
+
+ struct vop_reg enable;
+ struct vop_reg format;
+ struct vop_reg act_info;
+ struct vop_reg dsp_info;
+ struct vop_reg dsp_st;
+ struct vop_reg yrgb_mst;
+ struct vop_reg uv_mst;
+ struct vop_reg yrgb_vir;
+ struct vop_reg uv_vir;
+
+ struct vop_reg dst_alpha_ctl;
+ struct vop_reg src_alpha_ctl;
+};
+
+struct vop_win {
+ uint32_t base;
+ const struct vop_win_phy *phy;
+};
+
+struct vop_driver_data {
+ const void *init_table;
+ int table_size;
+ const struct vop_ctrl *ctrl;
+ const struct vop_win *win[VOP_MAX_WIN_SUPPORT];
+};
+
+static const uint32_t formats_01[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_NV12,
+ DRM_FORMAT_NV16,
+ DRM_FORMAT_NV24,
+};
+
+static const uint32_t formats_234[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_RGB565,
+};
+
+static const struct vop_win_phy win01_data = {
+ .data_formats = formats_01,
+ .nformats = ARRAY_SIZE(formats_01),
+ .enable = VOP_REG(WIN0_CTRL0, 0x1, 0),
+ .format = VOP_REG(WIN0_CTRL0, 0x7, 1),
+ .act_info = VOP_REG(WIN0_ACT_INFO, 0x1fff1fff, 0),
+ .dsp_info = VOP_REG(WIN0_DSP_INFO, 0x1fff1fff, 0),
+ .dsp_st = VOP_REG(WIN0_DSP_ST, 0x1fff1fff, 0),
+ .yrgb_mst = VOP_REG(WIN0_YRGB_MST, 0xffffffff, 0),
+ .uv_mst = VOP_REG(WIN0_CBR_MST, 0xffffffff, 0),
+ .yrgb_vir = VOP_REG(WIN0_VIR, 0x3fff, 0),
+ .uv_vir = VOP_REG(WIN0_VIR, 0x3fff, 16),
+ .src_alpha_ctl = VOP_REG(WIN0_SRC_ALPHA_CTRL, 0xff, 0),
+ .dst_alpha_ctl = VOP_REG(WIN0_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_phy win23_data = {
+ .data_formats = formats_234,
+ .nformats = ARRAY_SIZE(formats_234),
+ .enable = VOP_REG(WIN2_CTRL0, 0x1, 0),
+ .format = VOP_REG(WIN2_CTRL0, 0x7, 1),
+ .dsp_info = VOP_REG(WIN2_DSP_INFO0, 0x0fff0fff, 0),
+ .dsp_st = VOP_REG(WIN2_DSP_ST0, 0x1fff1fff, 0),
+ .yrgb_mst = VOP_REG(WIN2_MST0, 0xffffffff, 0),
+ .yrgb_vir = VOP_REG(WIN2_VIR0_1, 0x1fff, 0),
+ .src_alpha_ctl = VOP_REG(WIN2_SRC_ALPHA_CTRL, 0xff, 0),
+ .dst_alpha_ctl = VOP_REG(WIN2_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_phy cursor_data = {
+ .data_formats = formats_234,
+ .nformats = ARRAY_SIZE(formats_234),
+ .enable = VOP_REG(HWC_CTRL0, 0x1, 0),
+ .format = VOP_REG(HWC_CTRL0, 0x7, 1),
+ .dsp_st = VOP_REG(HWC_DSP_ST, 0x1fff1fff, 0),
+ .yrgb_mst = VOP_REG(HWC_MST, 0xffffffff, 0),
+};
+
+static const struct vop_win win0 = {
+ .base = 0,
+ .phy = &win01_data,
+};
+
+static const struct vop_win win1 = {
+ .base = 0x40,
+ .phy = &win01_data,
+};
+
+static const struct vop_win win2 = {
+ .base = 0,
+ .phy = &win23_data,
+};
+
+static const struct vop_win win3 = {
+ .base = 0x50,
+ .phy = &win23_data,
+};
+
+static const struct vop_win win_cursor = {
+ .base = 0,
+ .phy = &cursor_data,
+};
+
+static const struct vop_ctrl ctrl_data = {
+ .standby = VOP_REG(SYS_CTRL, 0x1, 22),
+ .gate_en = VOP_REG(SYS_CTRL, 0x1, 23),
+ .mmu_en = VOP_REG(SYS_CTRL, 0x1, 20),
+ .rgb_en = VOP_REG(SYS_CTRL, 0x1, 12),
+ .hdmi_en = VOP_REG(SYS_CTRL, 0x1, 13),
+ .edp_en = VOP_REG(SYS_CTRL, 0x1, 14),
+ .mipi_en = VOP_REG(SYS_CTRL, 0x1, 15),
+ .dither_down = VOP_REG(DSP_CTRL1, 0xf, 1),
+ .dither_up = VOP_REG(DSP_CTRL1, 0x1, 6),
+ .out_mode = VOP_REG(DSP_CTRL0, 0xf, 0),
+ .pin_pol = VOP_REG(DSP_CTRL0, 0xf, 4),
+ .htotal_pw = VOP_REG(DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+ .hact_st_end = VOP_REG(DSP_HACT_ST_END, 0x1fff1fff, 0),
+ .vtotal_pw = VOP_REG(DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+ .vact_st_end = VOP_REG(DSP_VACT_ST_END, 0x1fff1fff, 0),
+ .hpost_st_end = VOP_REG(POST_DSP_HACT_INFO, 0x1fff1fff, 0),
+ .vpost_st_end = VOP_REG(POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+};
+
+static const struct vop_reg_data vop_init_reg_table[] = {
+ {SYS_CTRL, 0x00801000},
+ {DSP_CTRL0, 0x00000000},
+ {WIN0_CTRL0, 0x00000080},
+ {WIN1_CTRL0, 0x00000080},
+};
+
+static const struct vop_driver_data rockchip_rk3288_vop = {
+ .init_table = vop_init_reg_table,
+ .table_size = ARRAY_SIZE(vop_init_reg_table),
+ .ctrl = &ctrl_data,
+ .win[0] = &win0,
+ .win[1] = &win1,
+ .win[2] = &win2,
+ .win[3] = &win3,
+ .win[4] = &win_cursor,
+};
+
+static const struct of_device_id vop_driver_dt_match[] = {
+ { .compatible = "rockchip,rk3288-vop",
+ .data = (void *)&rockchip_rk3288_vop },
+ {},
+};
+
+static inline void vop_writel(struct vop_context *ctx,
+ uint32_t offset, uint32_t v)
+{
+ writel(v, ctx->regs + offset);
+ ctx->regsbak[offset >> 2] = v;
+}
+
+static inline uint32_t vop_readl(struct vop_context *ctx, uint32_t offset)
+{
+ return readl(ctx->regs + offset);
+}
+
+static inline void vop_cfg_done(struct vop_context *ctx)
+{
+ writel(0x01, ctx->regs + REG_CFG_DONE);
+}
+
+static inline void vop_mask_write(struct vop_context *ctx,
+ uint32_t offset, uint32_t mask, uint32_t v)
+{
+ if (mask) {
+ uint32_t cached_val = ctx->regsbak[offset >> 2];
+
+ cached_val = (cached_val & ~mask) | v;
+ writel(cached_val, ctx->regs + offset);
+ ctx->regsbak[offset >> 2] = cached_val;
+ }
+}
+
+static inline struct vop_driver_data *vop_get_driver_data(struct device *dev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(vop_driver_dt_match, dev);
+
+ return (struct vop_driver_data *)of_id->data;
+}
+
+static enum vop_data_format vop_convert_format(uint32_t format)
+{
+ switch (format) {
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ARGB8888:
+ return VOP_FMT_ARGB8888;
+ case DRM_FORMAT_RGB888:
+ return VOP_FMT_RGB888;
+ case DRM_FORMAT_RGB565:
+ return VOP_FMT_RGB565;
+ case DRM_FORMAT_NV12:
+ return VOP_FMT_YUV420SP;
+ case DRM_FORMAT_NV16:
+ return VOP_FMT_YUV422SP;
+ case DRM_FORMAT_NV24:
+ return VOP_FMT_YUV444SP;
+ default:
+ DRM_ERROR("unsupport format[%08x]\n", format);
+ return -EINVAL;
+ }
+}
+
+static bool is_alpha_support(uint32_t format)
+{
+ switch (format) {
+ case DRM_FORMAT_ARGB8888:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* TODO(djkurtz): move generic 'setup slave rk_iommu' code somewhere common */
+int vop_iommu_init(struct vop_context *ctx)
+{
+ struct device *dev = ctx->dev;
+ struct device_node *np = dev->of_node;
+ struct platform_device *pd;
+ int count;
+ int ret;
+ struct of_phandle_args args;
+
+ /* Each VOP must have exactly one iommu node, with no args */
+ count = of_count_phandle_with_args(np, "iommus", "#iommu-cells");
+ if (count != 1) {
+ dev_err(dev, "of_count_phandle_with_args(%s) => %d\n",
+ np->full_name, count);
+ return -EINVAL;
+ }
+
+ ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0,
+ &args);
+ if (ret) {
+ dev_err(dev, "of_parse_phandle_with_args(%s) => %d\n",
+ np->full_name, ret);
+ return ret;
+ }
+ if (args.args_count != 0) {
+ dev_err(dev, "incorrect number of iommu params found for %s (found %d, expected 0)\n",
+ args.np->full_name, args.args_count);
+ return -EINVAL;
+ }
+
+ pd = of_find_device_by_node(args.np);
+ of_node_put(args.np);
+ if (!pd) {
+ dev_err(dev, "iommu %s not found\n", args.np->full_name);
+ return -EPROBE_DEFER;
+ }
+
+ /* TODO(djkurtz): handle multiple slave iommus for a single master */
+ dev->archdata.iommu = &pd->dev;
+
+ ret = rockchip_drm_dma_attach_device(ctx->drm_dev, dev);
+ if (ret) {
+ dev_err(dev, "failed to attach to drm dma mapping, %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void vop_iommu_fini(struct vop_context *ctx)
+{
+ rockchip_drm_dma_detach_device(ctx->drm_dev, ctx->dev);
+}
+
+static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
+{
+ 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 vop_clk_enable(struct vop_context *ctx)
+{
+ int ret;
+
+ if (!ctx->clk_on) {
+ ret = clk_prepare_enable(ctx->hclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to enable hclk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ctx->dclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to enable dclk\n");
+ goto err_dclk;
+ }
+
+ ret = clk_prepare_enable(ctx->aclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to enable aclk\n");
+ goto err_aclk;
+ }
+ ctx->clk_on = true;
+ }
+
+ return ret;
+err_aclk:
+ clk_disable_unprepare(ctx->aclk);
+err_dclk:
+ clk_disable_unprepare(ctx->hclk);
+ return ret;
+}
+
+static void vop_clk_disable(struct vop_context *ctx)
+{
+ if (ctx->clk_on) {
+ clk_disable_unprepare(ctx->dclk);
+ clk_disable_unprepare(ctx->hclk);
+ clk_disable_unprepare(ctx->aclk);
+ ctx->clk_on = false;
+ }
+}
+
+static void vop_power_on(struct vop_context *ctx)
+{
+ if (vop_clk_enable(ctx) < 0) {
+ dev_err(ctx->dev, "failed to enable clks\n");
+ return;
+ }
+
+ spin_lock(&ctx->reg_lock);
+
+ VOP_CTRL_SET(ctx, standby, 0);
+
+ spin_unlock(&ctx->reg_lock);
+}
+
+static void vop_power_off(struct vop_context *ctx)
+{
+ spin_lock(&ctx->reg_lock);
+
+ VOP_CTRL_SET(ctx, standby, 1);
+
+ spin_unlock(&ctx->reg_lock);
+
+ vop_clk_disable(ctx);
+}
+
+static int rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int crtc_x,
+ int crtc_y, unsigned int crtc_w,
+ unsigned int crtc_h, uint32_t src_x,
+ uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+ const struct vop_win *win = rockchip_plane->win;
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ struct drm_gem_object *obj;
+ struct rockchip_gem_object *rk_obj;
+ unsigned long offset;
+ unsigned int actual_w;
+ unsigned int actual_h;
+ unsigned int dsp_stx;
+ unsigned int dsp_sty;
+ unsigned int y_vir_stride;
+ dma_addr_t yrgb_mst;
+ enum vop_data_format format;
+ uint32_t val;
+ bool is_alpha;
+
+ if (!win) {
+ DRM_ERROR("can't find win data for vop, failed\n");
+ return -EINVAL;
+ }
+
+ obj = rockchip_fb_get_gem_obj(fb, 0);
+ if (!obj) {
+ DRM_ERROR("fail to get rockchip gem object from framebuffer\n");
+ return -EINVAL;
+ }
+
+ rk_obj = to_rockchip_obj(obj);
+
+ yrgb_mst = rk_obj->dma_addr;
+ if (yrgb_mst <= 0)
+ return -ENOMEM;
+
+ 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;
+ }
+
+ dsp_stx = crtc_x + crtc->mode.htotal - crtc->mode.hsync_start;
+ dsp_sty = crtc_y + crtc->mode.vtotal - crtc->mode.vsync_start;
+
+ offset = src_x * (fb->bits_per_pixel >> 3);
+ offset += src_y * fb->pitches[0];
+
+ y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3);
+ is_alpha = is_alpha_support(fb->pixel_format);
+ format = vop_convert_format(fb->pixel_format);
+
+ spin_lock(&ctx->reg_lock);
+
+ VOP_WIN_SET(ctx, win, format, format);
+ VOP_WIN_SET(ctx, win, yrgb_vir, y_vir_stride);
+ yrgb_mst += offset;
+ VOP_WIN_SET(ctx, win, yrgb_mst, yrgb_mst);
+ VOP_WIN_SET(ctx, win, act_info,
+ ((actual_h - 1) << 16) | (actual_w - 1));
+ VOP_WIN_SET(ctx, win, dsp_info,
+ ((actual_h - 1) << 16) | (actual_w - 1));
+ VOP_WIN_SET(ctx, win, dsp_st, (dsp_sty << 16) | dsp_stx);
+ if (is_alpha) {
+ VOP_WIN_SET(ctx, win, dst_alpha_ctl,
+ DST_FACTOR_M0(ALPHA_SRC_INVERSE));
+ val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
+ SRC_ALPHA_M0(ALPHA_STRAIGHT) |
+ SRC_BLEND_M0(ALPHA_PER_PIX) |
+ SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
+ SRC_FACTOR_M0(ALPHA_ONE);
+ VOP_WIN_SET(ctx, win, src_alpha_ctl, val);
+ } else {
+ VOP_WIN_SET(ctx, win, src_alpha_ctl, SRC_ALPHA_EN(0));
+ }
+
+ VOP_WIN_SET(ctx, win, enable, 1);
+
+ spin_unlock(&ctx->reg_lock);
+
+ mutex_lock(&ctx->vsync_mutex);
+
+ /*
+ * Because the buffer set to vop take effect at frame start time,
+ * we need make sure old buffer is not in use before we release
+ * it.
+ * reference the framebuffer, and unference it when it swap out of vop.
+ */
+ if (fb != rockchip_plane->front_fb) {
+ drm_framebuffer_reference(fb);
+ rockchip_plane->pending_fb = fb;
+ rockchip_plane->pending_yrgb_mst = yrgb_mst;
+ ctx->vsync_work_pending = true;
+ }
+ rockchip_plane->enabled = true;
+
+ mutex_unlock(&ctx->vsync_mutex);
+
+ spin_lock(&ctx->reg_lock);
+ vop_cfg_done(ctx);
+ spin_unlock(&ctx->reg_lock);
+
+ return 0;
+}
+
+static int rockchip_disable_plane(struct drm_plane *plane)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+ struct vop_context *ctx = rockchip_plane->ctx;
+ const struct vop_win *win = rockchip_plane->win;
+
+ spin_lock(&ctx->reg_lock);
+
+ VOP_WIN_SET(ctx, win, enable, 0);
+ vop_cfg_done(ctx);
+
+ spin_unlock(&ctx->reg_lock);
+
+ mutex_lock(&ctx->vsync_mutex);
+
+ /*
+ * clear the pending framebuffer and set vsync_work_pending true,
+ * so that the framebuffer will unref at the next vblank.
+ */
+ if (rockchip_plane->pending_fb) {
+ drm_framebuffer_unreference(rockchip_plane->pending_fb);
+ rockchip_plane->pending_fb = NULL;
+ }
+
+ rockchip_plane->enabled = false;
+ ctx->vsync_work_pending = true;
+
+ mutex_unlock(&ctx->vsync_mutex);
+
+ return 0;
+}
+
+static void rockchip_plane_destroy(struct drm_plane *plane)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+ struct vop_context *ctx = rockchip_plane->ctx;
+
+ rockchip_disable_plane(plane);
+ drm_plane_cleanup(plane);
+ ctx->win_mask &= ~(1 << rockchip_plane->id);
+ kfree(rockchip_plane);
+}
+
+static const struct drm_plane_funcs rockchip_plane_funcs = {
+ .update_plane = rockchip_update_plane,
+ .disable_plane = rockchip_disable_plane,
+ .destroy = rockchip_plane_destroy,
+};
+
+struct drm_plane *rockchip_plane_init(struct vop_context *ctx,
+ unsigned long possible_crtcs,
+ enum drm_plane_type type)
+{
+ struct rockchip_plane *rockchip_plane;
+ struct vop_driver_data *vop_data = ctx->data;
+ const struct vop_win *win;
+ int i;
+ int err;
+
+ rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
+ if (!rockchip_plane)
+ return NULL;
+
+ for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++) {
+ if (!(ctx->win_mask & (1 << i))) {
+ win = vop_data->win[i];
+ break;
+ }
+ }
+
+ if (VOP_MAX_WIN_SUPPORT == i) {
+ DRM_ERROR("failed to find win\n");
+ kfree(rockchip_plane);
+ return NULL;
+ }
+
+ ctx->win_mask |= (1 << i);
+ rockchip_plane->id = i;
+ rockchip_plane->win = win;
+ rockchip_plane->ctx = ctx;
+
+ err = drm_universal_plane_init(ctx->drm_dev, &rockchip_plane->base,
+ possible_crtcs, &rockchip_plane_funcs,
+ win->phy->data_formats,
+ win->phy->nformats, type);
+ if (err) {
+ DRM_ERROR("failed to initialize plane\n");
+ kfree(rockchip_plane);
+ return NULL;
+ }
+
+ return &rockchip_plane->base;
+}
+
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
+{
+ struct vop_context *ctx = to_vop_ctx(rockchip_find_crtc(dev, pipe));
+ unsigned long flags;
+
+ if (ctx->dpms != DRM_MODE_DPMS_ON)
+ return -EPERM;
+
+ spin_lock_irqsave(&ctx->irq_lock, flags);
+
+ vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
+ LINE_FLAG_INTR_EN(1));
+
+ spin_unlock_irqrestore(&ctx->irq_lock, flags);
+
+ return 0;
+}
+
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
+{
+ struct vop_context *ctx = to_vop_ctx(rockchip_find_crtc(dev, pipe));
+ unsigned long flags;
+
+ if (ctx->dpms != DRM_MODE_DPMS_ON)
+ return;
+ spin_lock_irqsave(&ctx->irq_lock, flags);
+ vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
+ LINE_FLAG_INTR_EN(0));
+ spin_unlock_irqrestore(&ctx->irq_lock, flags);
+}
+
+static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+
+ DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
+
+ if (ctx->dpms == mode) {
+ DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
+ return;
+ }
+ if (mode > DRM_MODE_DPMS_ON) {
+ /* wait for the completion of page flip. */
+ if (!wait_event_timeout(ctx->wait_vsync_queue,
+ !atomic_read(&ctx->wait_vsync_event),
+ HZ/20))
+ DRM_DEBUG_KMS("vblank wait timed out.\n");
+ drm_vblank_off(crtc->dev, ctx->pipe);
+ }
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ vop_power_on(ctx);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ vop_power_off(ctx);
+ break;
+ default:
+ DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+ break;
+ }
+
+ ctx->dpms = mode;
+}
+
+static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+ rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static bool rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* just do dummy now */
+
+ return true;
+}
+
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb);
+
+static int rockchip_drm_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *fb)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
+ u16 left_margin = adjusted_mode->htotal - adjusted_mode->hsync_end;
+ u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
+ u16 upper_margin = adjusted_mode->vtotal - adjusted_mode->vsync_end;
+ u16 hdisplay = adjusted_mode->hdisplay;
+ u16 vdisplay = adjusted_mode->vdisplay;
+ u16 htotal = adjusted_mode->htotal;
+ u16 vtotal = adjusted_mode->vtotal;
+ struct rockchip_display_mode *priv_mode =
+ (void *)adjusted_mode->private;
+ unsigned long flags;
+ int ret;
+ uint32_t val;
+
+ /* nothing to do if we haven't set the mode yet */
+ if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
+ return -EINVAL;
+
+ if (!priv_mode) {
+ DRM_ERROR("fail to found display output type[%d]\n",
+ priv_mode->out_type);
+ return -EINVAL;
+ }
+
+ ret = rockchip_drm_crtc_mode_set_base(crtc, x, y, fb);
+ if (ret)
+ return ret;
+
+ switch (priv_mode->out_type) {
+ case ROCKCHIP_DISPLAY_TYPE_RGB:
+ case ROCKCHIP_DISPLAY_TYPE_LVDS:
+ VOP_CTRL_SET(ctx, rgb_en, 1);
+ VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_P888);
+ break;
+ case ROCKCHIP_DISPLAY_TYPE_EDP:
+ VOP_CTRL_SET(ctx, edp_en, 1);
+ VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_AAAA);
+ break;
+ case ROCKCHIP_DISPLAY_TYPE_HDMI:
+ VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_AAAA);
+ VOP_CTRL_SET(ctx, hdmi_en, 1);
+ break;
+ default:
+ DRM_ERROR("unsupport out type[%d]\n", priv_mode->out_type);
+ return -EINVAL;
+ };
+
+ val = 0x8;
+ val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0;
+ val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? (1 << 1) : 0;
+ VOP_CTRL_SET(ctx, pin_pol, val);
+
+ VOP_CTRL_SET(ctx, htotal_pw, (htotal << 16) | hsync_len);
+ val = (hsync_len + left_margin) << 16;
+ val |= hsync_len + left_margin + hdisplay;
+ VOP_CTRL_SET(ctx, hact_st_end, val);
+ VOP_CTRL_SET(ctx, hpost_st_end, val);
+
+ VOP_CTRL_SET(ctx, vtotal_pw, (vtotal << 16) | vsync_len);
+ val = (vsync_len + upper_margin) << 16;
+ val |= vsync_len + upper_margin + vdisplay;
+ VOP_CTRL_SET(ctx, vact_st_end, val);
+ VOP_CTRL_SET(ctx, vpost_st_end, val);
+
+ spin_lock_irqsave(&ctx->irq_lock, flags);
+
+ vop_mask_write(ctx, INTR_CTRL0, DSP_LINE_NUM_MASK,
+ DSP_LINE_NUM(vsync_len + upper_margin + vdisplay));
+
+ spin_unlock_irqrestore(&ctx->irq_lock, flags);
+
+ clk_set_rate(ctx->dclk, adjusted_mode->clock * 1000);
+
+ return 0;
+}
+
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ unsigned int crtc_w;
+ unsigned int crtc_h;
+ int ret;
+
+ crtc_w = crtc->primary->fb->width - crtc->x;
+ crtc_h = crtc->primary->fb->height - crtc->y;
+
+ ret = rockchip_update_plane(crtc->primary, crtc, crtc->primary->fb, 0,
+ 0, crtc_w, crtc_h, crtc->x, crtc->y, crtc_w,
+ crtc_h);
+ if (ret < 0) {
+ DRM_ERROR("fail to update plane\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
+{
+ /* just do dummy now */
+}
+
+static const struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
+ .dpms = rockchip_drm_crtc_dpms,
+ .prepare = rockchip_drm_crtc_prepare,
+ .mode_fixup = rockchip_drm_crtc_mode_fixup,
+ .mode_set = rockchip_drm_crtc_mode_set,
+ .mode_set_base = rockchip_drm_crtc_mode_set_base,
+ .commit = rockchip_drm_crtc_commit,
+};
+
+static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event,
+ uint32_t page_flip_flags)
+{
+ struct drm_device *dev = crtc->dev;
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ struct drm_framebuffer *old_fb = crtc->primary->fb;
+ unsigned int crtc_w;
+ unsigned int crtc_h;
+ int ret;
+
+ /* when the page flip is requested, crtc's dpms should be on */
+ if (ctx->dpms > DRM_MODE_DPMS_ON) {
+ DRM_DEBUG("failed page flip request at dpms[%d].\n", ctx->dpms);
+ return 0;
+ }
+
+ ret = drm_vblank_get(dev, ctx->pipe);
+ if (ret) {
+ DRM_DEBUG("failed to acquire vblank counter\n");
+ return ret;
+ }
+
+ spin_lock_irq(&dev->event_lock);
+ if (ctx->event) {
+ spin_unlock_irq(&dev->event_lock);
+ DRM_ERROR("already pending flip!\n");
+ return -EBUSY;
+ }
+ ctx->event = event;
+ atomic_set(&ctx->wait_vsync_event, 1);
+ spin_unlock_irq(&dev->event_lock);
+
+ crtc->primary->fb = fb;
+ crtc_w = crtc->primary->fb->width - crtc->x;
+ crtc_h = crtc->primary->fb->height - crtc->y;
+
+ ret = rockchip_update_plane(crtc->primary, crtc, fb, 0, 0, crtc_w,
+ crtc_h, crtc->x, crtc->y, crtc_w, crtc_h);
+ if (ret) {
+ crtc->primary->fb = old_fb;
+
+ spin_lock_irq(&dev->event_lock);
+ drm_vblank_put(dev, ctx->pipe);
+ atomic_set(&ctx->wait_vsync_event, 0);
+ ctx->event = NULL;
+ spin_unlock_irq(&dev->event_lock);
+ }
+
+ return ret;
+}
+
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
+{
+ struct rockchip_drm_private *dev_priv = dev->dev_private;
+ struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
+ struct vop_context *ctx;
+ unsigned long flags;
+
+ if (!drm_crtc)
+ return;
+
+ ctx = to_vop_ctx(drm_crtc);
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+
+ if (ctx->event) {
+ drm_send_vblank_event(dev, -1, ctx->event);
+ drm_vblank_put(dev, pipe);
+ atomic_set(&ctx->wait_vsync_event, 0);
+ wake_up(&ctx->wait_vsync_queue);
+ ctx->event = NULL;
+ }
+
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev)
+{
+ int i;
+
+ for (i = 0; i < dev->num_crtcs; i++)
+ rockchip_drm_crtc_finish_pageflip(dev, i);
+}
+
+static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ struct rockchip_drm_private *private = crtc->dev->dev_private;
+
+ private->crtc[ctx->pipe] = NULL;
+ drm_crtc_cleanup(crtc);
+}
+
+static const struct drm_crtc_funcs rockchip_crtc_funcs = {
+ .set_config = drm_crtc_helper_set_config,
+ .page_flip = rockchip_drm_crtc_page_flip,
+ .destroy = rockchip_drm_crtc_destroy,
+};
+
+static void rockchip_vsync_worker(struct work_struct *work)
+{
+ struct vop_context *ctx = container_of(work, struct vop_context,
+ vsync_work);
+ struct drm_device *drm = ctx->drm_dev;
+ struct rockchip_drm_private *dev_priv = drm->dev_private;
+ struct drm_crtc *crtc = dev_priv->crtc[ctx->pipe];
+ struct rockchip_plane *rockchip_plane;
+ struct drm_plane *plane;
+ uint32_t yrgb_mst;
+
+ mutex_lock(&ctx->vsync_mutex);
+
+ ctx->vsync_work_pending = false;
+
+ list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
+ rockchip_plane = to_rockchip_plane(plane);
+
+ if (rockchip_plane->ctx != ctx)
+ continue;
+ if (rockchip_plane->enabled && !rockchip_plane->pending_fb)
+ continue;
+ if (!rockchip_plane->enabled && !rockchip_plane->front_fb)
+ continue;
+ /*
+ * make sure the yrgb_mst take effect, so that
+ * we can unreference the old framebuffer.
+ */
+ yrgb_mst = VOP_WIN_GET_YRGBADDR(ctx, rockchip_plane->win);
+ if (rockchip_plane->pending_yrgb_mst != yrgb_mst) {
+ /*
+ * some plane no complete, unref at next vblank
+ */
+ ctx->vsync_work_pending = true;
+ continue;
+ }
+
+ /*
+ * drm_framebuffer_unreference maybe call iommu unmap,
+ * and iommu not allow unmap buffer at irq context,
+ * so we do drm_framebuffer_unreference at queue_work.
+ */
+ if (rockchip_plane->front_fb)
+ drm_framebuffer_unreference(rockchip_plane->front_fb);
+
+ rockchip_plane->front_fb = rockchip_plane->pending_fb;
+ rockchip_plane->pending_fb = NULL;
+
+ /*
+ * if primary plane flip complete, sending the event to
+ * userspace
+ */
+ if (&rockchip_plane->base == crtc->primary)
+ rockchip_drm_crtc_finish_pageflip(ctx->drm_dev,
+ ctx->pipe);
+ }
+
+ mutex_unlock(&ctx->vsync_mutex);
+}
+
+static irqreturn_t rockchip_vop_isr(int irq, void *data)
+{
+ struct vop_context *ctx = data;
+ uint32_t intr0_reg;
+ unsigned long flags;
+
+ intr0_reg = vop_readl(ctx, INTR_CTRL0);
+ if (intr0_reg & LINE_FLAG_INTR) {
+ spin_lock_irqsave(&ctx->irq_lock, flags);
+ vop_writel(ctx, INTR_CTRL0, intr0_reg | LINE_FLAG_INTR_CLR);
+ spin_unlock_irqrestore(&ctx->irq_lock, flags);
+ } else {
+ return IRQ_NONE;
+ }
+
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+ if (ctx->vsync_work_pending)
+ queue_work(ctx->vsync_wq, &ctx->vsync_work);
+
+ return IRQ_HANDLED;
+}
+
+static int vop_create_crtc(struct vop_context *ctx)
+{
+ struct device *dev = ctx->dev;
+ struct drm_device *drm_dev = ctx->drm_dev;
+ struct drm_plane *primary, *cursor;
+ unsigned long possible_crtcs;
+ struct drm_crtc *crtc;
+ int ret;
+ int nr;
+
+ ctx->win_mask = 0;
+ crtc = &ctx->crtc;
+
+ ret = rockchip_drm_add_crtc(drm_dev, crtc, dev->of_node);
+ if (ret < 0)
+ return ret;
+ ctx->pipe = ret;
+
+ possible_crtcs = (1 << ctx->pipe);
+
+ primary = rockchip_plane_init(ctx, possible_crtcs,
+ DRM_PLANE_TYPE_PRIMARY);
+ if (!primary) {
+ DRM_ERROR("fail to init primary plane\n");
+ return -EINVAL;
+ }
+
+ for (nr = 1; nr < ROCKCHIP_MAX_PLANE; nr++) {
+ if (nr == VOP_DEFAULT_CURSOR) {
+ cursor = rockchip_plane_init(ctx, possible_crtcs,
+ DRM_PLANE_TYPE_CURSOR);
+ if (!cursor) {
+ DRM_ERROR("fail to init cursor plane\n");
+ return -EINVAL;
+ }
+ } else {
+ struct drm_plane *plane;
+
+ plane = rockchip_plane_init(ctx, possible_crtcs,
+ DRM_PLANE_TYPE_OVERLAY);
+ if (!plane) {
+ DRM_ERROR("fail to init overlay plane\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
+ &rockchip_crtc_funcs);
+ drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
+
+ return 0;
+}
+
+static int rockchip_vop_initial(struct vop_context *ctx)
+{
+ struct vop_driver_data *vop_data = ctx->data;
+ const struct vop_reg_data *init_table = vop_data->init_table;
+ struct reset_control *rst;
+ int i, ret;
+
+ ctx->hclk = devm_clk_get(ctx->dev, "hclk_vop");
+ if (IS_ERR(ctx->hclk)) {
+ dev_err(ctx->dev, "failed to get hclk source\n");
+ return PTR_ERR(ctx->hclk);
+ }
+ ctx->aclk = devm_clk_get(ctx->dev, "aclk_vop");
+ if (IS_ERR(ctx->aclk)) {
+ dev_err(ctx->dev, "failed to get aclk source\n");
+ return PTR_ERR(ctx->aclk);
+ }
+ ctx->dclk = devm_clk_get(ctx->dev, "dclk_vop");
+ if (IS_ERR(ctx->dclk)) {
+ dev_err(ctx->dev, "failed to get dclk source\n");
+ return PTR_ERR(ctx->dclk);
+ }
+
+ ret = vop_clk_enable(ctx);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * do hclk_reset, reset all vop registers.
+ */
+ rst = devm_reset_control_get(ctx->dev, "ahb");
+ if (IS_ERR(rst)) {
+ dev_err(ctx->dev, "failed to get ahb reset\n");
+ return PTR_ERR(rst);
+ }
+ reset_control_assert(rst);
+ usleep_range(10, 20);
+ reset_control_deassert(rst);
+
+ memcpy(ctx->regsbak, ctx->regs, ctx->len);
+
+ for (i = 0; i < vop_data->table_size; i++)
+ vop_writel(ctx, init_table[i].offset, init_table[i].value);
+
+ for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++)
+ VOP_WIN_SET(ctx, vop_data->win[i], enable, 0);
+
+ vop_cfg_done(ctx);
+
+ /*
+ * do dclk_reset, let all win config take affect, and then we can enable
+ * iommu safe.
+ */
+ rst = devm_reset_control_get(ctx->dev, "dclk");
+ if (IS_ERR(rst)) {
+ dev_err(ctx->dev, "failed to get dclk reset\n");
+ return PTR_ERR(rst);
+ }
+ reset_control_assert(rst);
+ usleep_range(10, 20);
+ reset_control_deassert(rst);
+
+ ctx->dpms = DRM_MODE_DPMS_ON;
+
+ return 0;
+}
+
+static int vop_bind(struct device *dev, struct device *master, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vop_driver_data *vop_data = vop_get_driver_data(dev);
+ struct drm_device *drm_dev = data;
+ struct vop_context *ctx;
+ struct resource *res;
+ int ret;
+
+ if (!vop_data)
+ return -ENODEV;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = dev;
+ ctx->data = vop_data;
+ ctx->drm_dev = drm_dev;
+ dev_set_drvdata(dev, ctx);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ctx->len = resource_size(res);
+ ctx->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctx->regs))
+ return PTR_ERR(ctx->regs);
+
+ ctx->regsbak = devm_kzalloc(dev, ctx->len, GFP_KERNEL);
+ if (!ctx->regsbak)
+ return -ENOMEM;
+
+ ret = rockchip_vop_initial(ctx);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
+ return ret;
+ }
+
+ ctx->irq = platform_get_irq(pdev, 0);
+ if (ctx->irq < 0) {
+ dev_err(dev, "cannot find irq for vop\n");
+ return ctx->irq;
+ }
+
+ spin_lock_init(&ctx->reg_lock);
+ spin_lock_init(&ctx->irq_lock);
+
+ init_waitqueue_head(&ctx->wait_vsync_queue);
+ atomic_set(&ctx->wait_vsync_event, 0);
+
+ ret = vop_iommu_init(ctx);
+ if (ret) {
+ DRM_ERROR("Failed to setup iommu, %d\n", ret);
+ return ret;
+ }
+
+ ctx->vsync_wq = create_singlethread_workqueue("vsync");
+ if (!ctx->vsync_wq) {
+ dev_err(dev, "failed to create workqueue\n");
+ return -EINVAL;
+ }
+ INIT_WORK(&ctx->vsync_work, rockchip_vsync_worker);
+
+ mutex_init(&ctx->vsync_mutex);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_request_irq(dev, ctx->irq, rockchip_vop_isr,
+ IRQF_SHARED, dev_name(dev), ctx);
+ if (ret) {
+ dev_err(dev, "cannot requeset irq%d - err %d\n", ctx->irq, ret);
+ return ret;
+ }
+
+ return vop_create_crtc(ctx);
+}
+
+static void vop_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct drm_device *drm_dev = data;
+ struct vop_context *ctx = dev_get_drvdata(dev);
+ struct drm_crtc *crtc = &ctx->crtc;
+
+ drm_crtc_cleanup(crtc);
+ pm_runtime_disable(dev);
+ rockchip_drm_remove_crtc(drm_dev, ctx->pipe);
+
+ vop_iommu_fini(ctx);
+}
+
+static const struct component_ops vop_component_ops = {
+ .bind = vop_bind,
+ .unbind = vop_unbind,
+};
+
+static int vop_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vop_context *ctx;
+
+ if (!dev->of_node) {
+ dev_err(dev, "can't find vop devices\n");
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, ctx);
+
+ return component_add(dev, &vop_component_ops);
+}
+
+static int vop_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &vop_component_ops);
+
+ return 0;
+}
+
+struct platform_driver rockchip_vop_platform_driver = {
+ .probe = vop_probe,
+ .remove = vop_remove,
+ .driver = {
+ .name = "rockchip-vop",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(vop_driver_dt_match),
+ },
+};
+
+module_platform_driver(rockchip_vop_platform_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP VOP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
new file mode 100644
index 0000000..2343760
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_VOP_H
+#define _ROCKCHIP_DRM_VOP_H
+
+/* register definition */
+#define REG_CFG_DONE 0x0000
+#define VERSION_INFO 0x0004
+#define SYS_CTRL 0x0008
+#define SYS_CTRL1 0x000c
+#define DSP_CTRL0 0x0010
+#define DSP_CTRL1 0x0014
+#define DSP_BG 0x0018
+#define MCU_CTRL 0x001c
+#define INTR_CTRL0 0x0020
+#define INTR_CTRL1 0x0024
+#define WIN0_CTRL0 0x0030
+#define WIN0_CTRL1 0x0034
+#define WIN0_COLOR_KEY 0x0038
+#define WIN0_VIR 0x003c
+#define WIN0_YRGB_MST 0x0040
+#define WIN0_CBR_MST 0x0044
+#define WIN0_ACT_INFO 0x0048
+#define WIN0_DSP_INFO 0x004c
+#define WIN0_DSP_ST 0x0050
+#define WIN0_SCL_FACTOR_YRGB 0x0054
+#define WIN0_SCL_FACTOR_CBR 0x0058
+#define WIN0_SCL_OFFSET 0x005c
+#define WIN0_SRC_ALPHA_CTRL 0x0060
+#define WIN0_DST_ALPHA_CTRL 0x0064
+#define WIN0_FADING_CTRL 0x0068
+/* win1 register */
+#define WIN1_CTRL0 0x0070
+#define WIN1_CTRL1 0x0074
+#define WIN1_COLOR_KEY 0x0078
+#define WIN1_VIR 0x007c
+#define WIN1_YRGB_MST 0x0080
+#define WIN1_CBR_MST 0x0084
+#define WIN1_ACT_INFO 0x0088
+#define WIN1_DSP_INFO 0x008c
+#define WIN1_DSP_ST 0x0090
+#define WIN1_SCL_FACTOR_YRGB 0x0094
+#define WIN1_SCL_FACTOR_CBR 0x0098
+#define WIN1_SCL_OFFSET 0x009c
+#define WIN1_SRC_ALPHA_CTRL 0x00a0
+#define WIN1_DST_ALPHA_CTRL 0x00a4
+#define WIN1_FADING_CTRL 0x00a8
+/* win2 register */
+#define WIN2_CTRL0 0x00b0
+#define WIN2_CTRL1 0x00b4
+#define WIN2_VIR0_1 0x00b8
+#define WIN2_VIR2_3 0x00bc
+#define WIN2_MST0 0x00c0
+#define WIN2_DSP_INFO0 0x00c4
+#define WIN2_DSP_ST0 0x00c8
+#define WIN2_COLOR_KEY 0x00cc
+#define WIN2_MST1 0x00d0
+#define WIN2_DSP_INFO1 0x00d4
+#define WIN2_DSP_ST1 0x00d8
+#define WIN2_SRC_ALPHA_CTRL 0x00dc
+#define WIN2_MST2 0x00e0
+#define WIN2_DSP_INFO2 0x00e4
+#define WIN2_DSP_ST2 0x00e8
+#define WIN2_DST_ALPHA_CTRL 0x00ec
+#define WIN2_MST3 0x00f0
+#define WIN2_DSP_INFO3 0x00f4
+#define WIN2_DSP_ST3 0x00f8
+#define WIN2_FADING_CTRL 0x00fc
+/* win3 register */
+#define WIN3_CTRL0 0x0100
+#define WIN3_CTRL1 0x0104
+#define WIN3_VIR0_1 0x0108
+#define WIN3_VIR2_3 0x010c
+#define WIN3_MST0 0x0110
+#define WIN3_DSP_INFO0 0x0114
+#define WIN3_DSP_ST0 0x0118
+#define WIN3_COLOR_KEY 0x011c
+#define WIN3_MST1 0x0120
+#define WIN3_DSP_INFO1 0x0124
+#define WIN3_DSP_ST1 0x0128
+#define WIN3_SRC_ALPHA_CTRL 0x012c
+#define WIN3_MST2 0x0130
+#define WIN3_DSP_INFO2 0x0134
+#define WIN3_DSP_ST2 0x0138
+#define WIN3_DST_ALPHA_CTRL 0x013c
+#define WIN3_MST3 0x0140
+#define WIN3_DSP_INFO3 0x0144
+#define WIN3_DSP_ST3 0x0148
+#define WIN3_FADING_CTRL 0x014c
+/* hwc register */
+#define HWC_CTRL0 0x0150
+#define HWC_CTRL1 0x0154
+#define HWC_MST 0x0158
+#define HWC_DSP_ST 0x015c
+#define HWC_SRC_ALPHA_CTRL 0x0160
+#define HWC_DST_ALPHA_CTRL 0x0164
+#define HWC_FADING_CTRL 0x0168
+/* post process register */
+#define POST_DSP_HACT_INFO 0x0170
+#define POST_DSP_VACT_INFO 0x0174
+#define POST_SCL_FACTOR_YRGB 0x0178
+#define POST_SCL_CTRL 0x0180
+#define POST_DSP_VACT_INFO_F1 0x0184
+#define DSP_HTOTAL_HS_END 0x0188
+#define DSP_HACT_ST_END 0x018c
+#define DSP_VTOTAL_VS_END 0x0190
+#define DSP_VACT_ST_END 0x0194
+#define DSP_VS_ST_END_F1 0x0198
+#define DSP_VACT_ST_END_F1 0x019c
+/* register definition end */
+
+/* interrupt define */
+#define DSP_HOLD_VALID_INTR (1 << 0)
+#define FS_INTR (1 << 1)
+#define LINE_FLAG_INTR (1 << 2)
+#define BUS_ERROR_INTR (1 << 3)
+
+#define DSP_HOLD_VALID_INTR_EN(x) ((x) << 4)
+#define FS_INTR_EN(x) ((x) << 5)
+#define LINE_FLAG_INTR_EN(x) ((x) << 6)
+#define BUS_ERROR_INTR_EN(x) ((x) << 7)
+#define DSP_HOLD_VALID_INTR_MASK (1 << 4)
+#define FS_INTR_EN_MASK (1 << 5)
+#define LINE_FLAG_INTR_MASK (1 << 6)
+#define BUS_ERROR_INTR_MASK (1 << 7)
+
+#define DSP_HOLD_VALID_INTR_CLR (1 << 8)
+#define FS_INTR_EN_CLR (1 << 9)
+#define LINE_FLAG_INTR_CLR (1 << 10)
+#define BUS_ERROR_INTR_CLR (1 << 11)
+#define DSP_LINE_NUM(x) (((x) & 0x1fff) << 12)
+#define DSP_LINE_NUM_MASK (0x1fff << 12)
+
+/* src alpha ctrl define */
+#define SRC_FADING_VALUE(x) (((x) & 0xff) << 24)
+#define SRC_GLOBAL_ALPHA(x) (((x) & 0xff) << 16)
+#define SRC_FACTOR_M0(x) (((x) & 0x7) << 6)
+#define SRC_ALPHA_CAL_M0(x) (((x) & 0x1) << 5)
+#define SRC_BLEND_M0(x) (((x) & 0x3) << 3)
+#define SRC_ALPHA_M0(x) (((x) & 0x1) << 2)
+#define SRC_COLOR_M0(x) (((x) & 0x1) << 1)
+#define SRC_ALPHA_EN(x) (((x) & 0x1) << 0)
+/* dst alpha ctrl define */
+#define DST_FACTOR_M0(x) (((x) & 0x7) << 6)
+
+enum alpha_mode {
+ ALPHA_STRAIGHT,
+ ALPHA_INVERSE,
+};
+
+enum global_blend_mode {
+ ALPHA_GLOBAL,
+ ALPHA_PER_PIX,
+ ALPHA_PER_PIX_GLOBAL,
+};
+
+enum alpha_cal_mode {
+ ALPHA_SATURATION,
+ ALPHA_NO_SATURATION,
+};
+
+enum color_mode {
+ ALPHA_SRC_PRE_MUL,
+ ALPHA_SRC_NO_PRE_MUL,
+};
+
+enum factor_mode {
+ ALPHA_ZERO,
+ ALPHA_ONE,
+ ALPHA_SRC,
+ ALPHA_SRC_INVERSE,
+ ALPHA_SRC_GLOBAL,
+};
+
+#endif /* _ROCKCHIP_DRM_VOP_H */
--
1.7.9.5
^ permalink raw reply related
* [PATCH v5 0/3] Add drm driver for Rockchip Socs
From: Mark yao @ 2014-09-24 2:11 UTC (permalink / raw)
To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Boris BREZILLON, David Airlie,
Rob Clark, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
Kumar Gala, Randy Dunlap, Grant Likely, Greg Kroah-Hartman,
John Stultz, Rom Lemarchand
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
xxm-TNX95d0MmH7DzftRWevZcw, huangtao-TNX95d0MmH7DzftRWevZcw,
kever.yang-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, Mark yao
This a series of patches is a DRM Driver for Rockchip Socs, add support
for vop devices. Future patches will add additional encoders/connectors,
such as eDP, HDMI.
The basic "crtc" for rockchip is a "VOP" - Video Output Processor.
the vop devices found on Rockchip rk3288 Soc, rk3288 soc have two similar
Vop devices. Vop devices support iommu mapping, we use dma-mapping API with
ARM_DMA_USE_IOMMU.
Changes in v2:
- add DRM master device node to list all display nodes that comprise
the graphics subsystem.
- use the component framework to defer main drm driver probe
until all VOP devices have been probed.
- use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
master device and each vop device can shared the drm dma mapping.
- use drm_crtc_init_with_planes and drm_universal_plane_init.
- remove unnecessary middle layers.
- add cursor set, move funcs to rockchip drm crtc.
- add vop reset.
Changes in v3:
- change "crtc->fb" to "crtc->primary-fb"
Adviced by Daniel Vetter
- init cursor plane with universal api, remove unnecessary cursor set,move
Changes in v4:
Adviced by David Herrmann
- remove drm_platform_*() usage, use register drm device directly.
Adviced by Rob Clark
- remove special mmap ioctl, do userspace mmap with normal mmap() or mmap offset
Changes in v5:
Adviced by Arnd Bergmann
- doing DMA start with a 32-bit masks with dma_mask and dma_coherent_mark
- fix some incorrect dependencies.
Adviced by Boris BREZILLON
- fix some mistake and bugs.
Adviced by Daniel Vetter
- drop all special ioctl and use generic kms ioctl instead.
Adviced by Rob Clark
- use unlocked api for drm_fb_helper_restore_fbdev_mode.
- remove unused rockchip_gem_prime_import_sg_table.
Mark yao (3):
drm/rockchip: Add basic drm driver
dt-bindings: video: Add for rockchip display subsytem
dt-bindings: video: Add documentation for rockchip vop
.../devicetree/bindings/video/rockchip-drm.txt | 19 +
.../devicetree/bindings/video/rockchip-vop.txt | 58 +
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/rockchip/Kconfig | 17 +
drivers/gpu/drm/rockchip/Makefile | 8 +
drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 509 ++++++++
drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 120 ++
drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 201 +++
drivers/gpu/drm/rockchip/rockchip_drm_fb.h | 28 +
drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 230 ++++
drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 20 +
drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 341 +++++
drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 55 +
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 1373 ++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 187 +++
16 files changed, 3169 insertions(+)
--
1.7.9.5
^ permalink raw reply
* Re: [PATCH v4 5/5] drm/rockchip: Add support for Rockchip Soc EDP
From: Rob Clark @ 2014-09-23 23:35 UTC (permalink / raw)
To: cym
Cc: Mark yao, Heiko Stübner, Boris BREZILLON, David Airlie,
Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
Randy Dunlap, Grant Likely, Greg Kroah-Hartman, John Stultz,
Rom Lemarchand,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-doc-u79uwXL29TY76Z2rM5mHXA, Linux Kernel Mailing List,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Douglas Anderson
In-Reply-To: <CAF6AEGuGKitXGE8S-fE-d+-MWDBxU-XqG_AH7q9FD0y5YkBFVQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Tue, Sep 23, 2014 at 9:56 AM, Rob Clark <robdclark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Tue, Sep 23, 2014 at 4:47 AM, cym <cym-TNX95d0MmH7DzftRWevZcw@public.gmane.org> wrote:
>>
>> On Tuesday, September 23, 2014 03:20 AM, Rob Clark wrote:
>>>
>>> On Mon, Sep 22, 2014 at 7:02 AM, Mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org> wrote:
>>>>
>>>> This adds support for Rockchip soc edp found on rk3288
>>>>
>>>> Signed-off-by: Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>>>> Signed-off-by: Jeff Chen <jeff.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>>>> ---
>>>> Changes in v2:
>>>> - fix code sytle
>>>> - use some define from drm_dp_helper.h
>>>> - use panel-simple driver for primary display.
>>>> - remove unnecessary clock clk_24m_parent.
>>>>
>>>> Changes in v3: None
>>>>
>>>> Changes in v4: None
>>>>
>>>> drivers/gpu/drm/rockchip/Kconfig | 9 +
>>>> drivers/gpu/drm/rockchip/Makefile | 2 +
>>>> drivers/gpu/drm/rockchip/rockchip_edp_core.c | 853 ++++++++++++++++++
>>>> drivers/gpu/drm/rockchip/rockchip_edp_core.h | 309 +++++++
>>>> drivers/gpu/drm/rockchip/rockchip_edp_reg.c | 1202
>>>> ++++++++++++++++++++++++++
>>>> drivers/gpu/drm/rockchip/rockchip_edp_reg.h | 345 ++++++++
>>>> 6 files changed, 2720 insertions(+)
>>>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.c
>>>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_core.h
>>>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.c
>>>> create mode 100644 drivers/gpu/drm/rockchip/rockchip_edp_reg.h
>>>>
>>>> diff --git a/drivers/gpu/drm/rockchip/Kconfig
>>>> b/drivers/gpu/drm/rockchip/Kconfig
>>>> index 7146c80..04b1f8c 100644
>>>> --- a/drivers/gpu/drm/rockchip/Kconfig
>>>> +++ b/drivers/gpu/drm/rockchip/Kconfig
>>>> @@ -17,3 +17,12 @@ config DRM_ROCKCHIP
>>>> management to userspace. This driver does not provides
>>>> 2D or 3D acceleration; acceleration is performed by other
>>>> IP found on the SoC.
>>>> +
>>>> +config ROCKCHIP_EDP
>>>> + bool "Rockchip edp support"
>>>> + depends on DRM_ROCKCHIP
>>>> + help
>>>> + Choose this option if you have a Rockchip eDP.
>>>> + Rockchip rk3288 SoC has eDP TX Controller can be used.
>>>> + If you have an Embedded DisplayPort Panel, say Y to enable its
>>>> + driver.
>>>> diff --git a/drivers/gpu/drm/rockchip/Makefile
>>>> b/drivers/gpu/drm/rockchip/Makefile
>>>> index 6e6d468..a0fc3a1 100644
>>>> --- a/drivers/gpu/drm/rockchip/Makefile
>>>> +++ b/drivers/gpu/drm/rockchip/Makefile
>>>> @@ -7,4 +7,6 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
>>>> rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o
>>>> rockchip_drm_fbdev.o \
>>>> rockchip_drm_gem.o rockchip_drm_vop.o
>>>>
>>>> +rockchipdrm-$(CONFIG_ROCKCHIP_EDP) += rockchip_edp_core.o
>>>> rockchip_edp_reg.o
>>>> +
>>>> obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
>>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_edp_core.c
>>>> b/drivers/gpu/drm/rockchip/rockchip_edp_core.c
>>>> new file mode 100644
>>>> index 0000000..5450d1fa
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/rockchip/rockchip_edp_core.c
>>>> @@ -0,0 +1,853 @@
>>>> +/*
>>>> +* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>>>> +* Author:
>>>> +* Andy yan <andy.yan-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>>>> +* Jeff chen <jeff.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>>>> +*
>>>> +* based on exynos_dp_core.c
>>>> +*
>>>
>>> hmm, did you look at all at drm_dp_helpers? The exynos code probably
>>> pre-dates the helpers, so might not be the best example to work off
>>> of..
>>>
>>> If there is actually a valid reason not to use the dp-helpers, then
>>> you should mention the reasons, at least in the commit msg if not the
>>> code
>>>
>>> BR,
>>> -R
>>
>> Thanks Rob,Because RK3288 eDP controller IP design is similar to exynos.They
>> from same IP vendors but have some difference.
>> So we choosed exynos_dp as example to work off of.exynos_dp only used some
>> defines from drm_dp_helper.h like DPCD.
>>
>
>
> Hmm, it sounds like it perhaps should be refactored out into a
> drm_bridge so more of it can be shared between rockchip and exynos.
>
> Either way, it should be using the drm_dp_helpers.. That "the code I
> copied did it wrong" isn't a terribly good reason for new drivers to
> do it wrong.
>
> So NAK for the eDP part until you use the helpers.
and btw, if it wasn't clear, go ahead and at least repost the core
part of the driver.. the first patch just needed a few small tweaks to
get my r-b even if it takes longer to sort out something sane for the
DP part..
BR,
-R
^ permalink raw reply
* Re: [PATCH v2 5/7] selftests/mount: change test to use ksft framework
From: Shuah Khan @ 2014-09-23 23:18 UTC (permalink / raw)
To: Eric W. Biederman
Cc: akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
colin.king-Z7WLFzj8eWMS+FvcfC7Uqw, dbueso-l3A5Bk7waGM,
serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA,
thierry-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
luto-kltTT9wpgjJwATOyAt5JVQ, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Shuah Khan
In-Reply-To: <8761geylzj.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>
On 09/23/2014 05:07 PM, Eric W. Biederman wrote:
> Shuah Khan <shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org> writes:
>
>> On 09/23/2014 04:41 PM, Eric W. Biederman wrote:
>>> Shuah Khan <shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org> writes:
>>>
>>>> Change mount test to use kselftest framework to report
>>>> test results.
>>>
>>> Nacked-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
>>>
>>> I am curious did you even run these tests? I can't possibly see how
>>> the tests would have passed with this change. At the very least you
>>> have taken this test from linear to exponential time complexity.
>>>
>>
>> Oops. I meant to drop this commit from the series. Sorry for the
>> noise.
>
> No problem. Except for the unnecessary semantic change the rest of the
> change looked fine. I just wanted to make certain you didn't break my
> test by accident.
>
Thanks for catching it. The results didn't look right and I meant
to delay this change for later after another round of testing.
I will fix it without the unnecessary change and send it out
separately.
thanks,
-- Shuah
--
Shuah Khan
Sr. Linux Kernel Developer
Samsung Research America (Silicon Valley)
shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org | (970) 217-8978
^ permalink raw reply
* Re: [PATCH v2 5/7] selftests/mount: change test to use ksft framework
From: Eric W. Biederman @ 2014-09-23 23:07 UTC (permalink / raw)
To: Shuah Khan
Cc: akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
colin.king-Z7WLFzj8eWMS+FvcfC7Uqw, dbueso-l3A5Bk7waGM,
serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA,
thierry-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
luto-kltTT9wpgjJwATOyAt5JVQ, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <5421FA8A.9050209-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
Shuah Khan <shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org> writes:
> On 09/23/2014 04:41 PM, Eric W. Biederman wrote:
>> Shuah Khan <shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org> writes:
>>
>>> Change mount test to use kselftest framework to report
>>> test results.
>>
>> Nacked-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
>>
>> I am curious did you even run these tests? I can't possibly see how
>> the tests would have passed with this change. At the very least you
>> have taken this test from linear to exponential time complexity.
>>
>
> Oops. I meant to drop this commit from the series. Sorry for the
> noise.
No problem. Except for the unnecessary semantic change the rest of the
change looked fine. I just wanted to make certain you didn't break my
test by accident.
Eric
^ permalink raw reply
* Re: [PATCHv7 10/26] vfio: platform: probe to devices on the platform bus
From: Alex Williamson @ 2014-09-23 23:01 UTC (permalink / raw)
To: Antonios Motakis
Cc: kvm-u79uwXL29TY76Z2rM5mHXA, eric.auger-QSEj5FYQhm4dnm+yROfE0A,
marc.zyngier-5wv7dgnIgG8, open list:ABI/API,
will.deacon-5wv7dgnIgG8, open list,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
tech-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J,
kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg,
christoffer.dall-QSEj5FYQhm4dnm+yROfE0A
In-Reply-To: <1411483586-29304-11-git-send-email-a.motakis-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J@public.gmane.org>
On Tue, 2014-09-23 at 16:46 +0200, Antonios Motakis wrote:
> Driver to bind to Linux platform devices, and callbacks to discover their
> resources to be used by the main VFIO PLATFORM code.
>
> Signed-off-by: Antonios Motakis <a.motakis-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J@public.gmane.org>
> ---
> drivers/vfio/platform/vfio_platform.c | 96 +++++++++++++++++++++++++++++++++++
> include/uapi/linux/vfio.h | 1 +
> 2 files changed, 97 insertions(+)
> create mode 100644 drivers/vfio/platform/vfio_platform.c
>
> diff --git a/drivers/vfio/platform/vfio_platform.c b/drivers/vfio/platform/vfio_platform.c
> new file mode 100644
> index 0000000..024c026
> --- /dev/null
> +++ b/drivers/vfio/platform/vfio_platform.c
> @@ -0,0 +1,96 @@
> +/*
> + * Copyright (C) 2013 - Virtual Open Systems
> + * Author: Antonios Motakis <a.motakis-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License, version 2, as
> + * published by the Free Software Foundation.
> + *
> + * 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/device.h>
> +#include <linux/eventfd.h>
> +#include <linux/interrupt.h>
> +#include <linux/iommu.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/notifier.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/uaccess.h>
> +#include <linux/vfio.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <linux/irq.h>
> +
> +#include "vfio_platform_private.h"
> +
> +#define DRIVER_VERSION "0.7"
> +#define DRIVER_AUTHOR "Antonios Motakis <a.motakis-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J@public.gmane.org>"
> +#define DRIVER_DESC "VFIO for platform devices - User Level meta-driver"
> +
> +/* probing devices from the linux platform bus */
> +
> +static struct resource *get_platform_resource(struct vfio_platform_device *vdev,
> + int i)
> +{
> + struct platform_device *pdev = (struct platform_device *) vdev->opaque;
> +
> + return platform_get_resource(pdev, IORESOURCE_MEM, i);
ARM may only support IORESOURCE_MEM, but I don't think platform devices
are limited to MMIO, right? vfio-platform shouldn't be either.
> +}
> +
> +static int get_platform_irq(struct vfio_platform_device *vdev, int i)
> +{
> + struct platform_device *pdev = (struct platform_device *) vdev->opaque;
> +
> + return platform_get_irq(pdev, i);
> +}
> +
> +
> +static int vfio_platform_probe(struct platform_device *pdev)
> +{
> + struct vfio_platform_device *vdev;
> + int ret;
> +
> + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
> + if (!vdev)
> + return -ENOMEM;
> +
> + vdev->opaque = (void *) pdev;
> + vdev->name = pdev->name;
> + vdev->flags = VFIO_DEVICE_FLAGS_PLATFORM;
> + vdev->get_resource = get_platform_resource;
> + vdev->get_irq = get_platform_irq;
> +
> + ret = vfio_platform_probe_common(vdev, &pdev->dev);
> + if (ret)
> + kfree(vdev);
> +
> + return ret;
> +}
> +
> +static int vfio_platform_remove(struct platform_device *pdev)
> +{
> + return vfio_platform_remove_common(&pdev->dev);
> +}
> +
> +static struct platform_driver vfio_platform_driver = {
> + .probe = vfio_platform_probe,
> + .remove = vfio_platform_remove,
> + .driver = {
> + .name = "vfio-platform",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +module_platform_driver(vfio_platform_driver);
> +
> +MODULE_VERSION(DRIVER_VERSION);
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
> index 30f630c..b022a25 100644
> --- a/include/uapi/linux/vfio.h
> +++ b/include/uapi/linux/vfio.h
> @@ -158,6 +158,7 @@ struct vfio_device_info {
> __u32 flags;
> #define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */
> #define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */
> +#define VFIO_DEVICE_FLAGS_PLATFORM (1 << 2) /* vfio-platform device */
> __u32 num_regions; /* Max region index + 1 */
> __u32 num_irqs; /* Max IRQ index + 1 */
> };
^ permalink raw reply
* Re: [PATCH v2 5/7] selftests/mount: change test to use ksft framework
From: Shuah Khan @ 2014-09-23 22:56 UTC (permalink / raw)
To: Eric W. Biederman
Cc: akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
colin.king-Z7WLFzj8eWMS+FvcfC7Uqw, dbueso-l3A5Bk7waGM,
serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA,
thierry-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
luto-kltTT9wpgjJwATOyAt5JVQ, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Shuah Khan
In-Reply-To: <87d2am2c4v.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>
On 09/23/2014 04:41 PM, Eric W. Biederman wrote:
> Shuah Khan <shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org> writes:
>
>> Change mount test to use kselftest framework to report
>> test results.
>
> Nacked-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
>
> I am curious did you even run these tests? I can't possibly see how
> the tests would have passed with this change. At the very least you
> have taken this test from linear to exponential time complexity.
>
Oops. I meant to drop this commit from the series. Sorry for the noise.
-- Shuah
--
Shuah Khan
Sr. Linux Kernel Developer
Samsung Research America (Silicon Valley)
shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org | (970) 217-8978
^ permalink raw reply
* Re: [PATCH v2 5/7] selftests/mount: change test to use ksft framework
From: Eric W. Biederman @ 2014-09-23 22:41 UTC (permalink / raw)
To: Shuah Khan
Cc: akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
colin.king-Z7WLFzj8eWMS+FvcfC7Uqw, dbueso-l3A5Bk7waGM,
serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA,
thierry-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
luto-kltTT9wpgjJwATOyAt5JVQ, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <969f0780734a04763f2b54063603b93380244d0c.1411506121.git.shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
Shuah Khan <shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org> writes:
> Change mount test to use kselftest framework to report
> test results.
Nacked-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
I am curious did you even run these tests? I can't possibly see how
the tests would have passed with this change. At the very least you
have taken this test from linear to exponential time complexity.
> Signed-off-by: Shuah Khan <shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
> ---
> tools/testing/selftests/mount/unprivileged-remount-test.c | 8 +++++---
> 1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/tools/testing/selftests/mount/unprivileged-remount-test.c b/tools/testing/selftests/mount/unprivileged-remount-test.c
> index 1b3ff2f..c64d442 100644
> --- a/tools/testing/selftests/mount/unprivileged-remount-test.c
> +++ b/tools/testing/selftests/mount/unprivileged-remount-test.c
> @@ -13,6 +13,8 @@
> #include <stdbool.h>
> #include <stdarg.h>
>
> +#include "../kselftest.h"
> +
> #ifndef CLONE_NEWNS
> # define CLONE_NEWNS 0x00020000
> #endif
> @@ -45,7 +47,7 @@ static void die(char *fmt, ...)
> va_start(ap, fmt);
> vfprintf(stderr, fmt, ap);
> va_end(ap);
> - exit(EXIT_FAILURE);
> + ksft_exit_fail();
> }
>
> static void write_file(char *filename, char *fmt, ...)
> @@ -176,7 +178,7 @@ bool test_unpriv_remount(int mount_flags, int remount_flags, int invalid_flags)
> die("remount of /tmp with invalid flags "
> "succeeded unexpectedly\n");
> }
> - exit(EXIT_SUCCESS);
> + return ksft_exit_pass();
This change is a deep bug.
This exit is a forked child process (not from the test itself). That a
few lines earlier in the test I test the value of in waitpid.
Also in your change commit comment if no where else you should have
called out that you were making a semantic change to the test.
> }
>
> static bool test_unpriv_remount_simple(int mount_flags)
> @@ -238,5 +240,5 @@ int main(int argc, char **argv)
> {
> die("Default atime malfunctions\n");
> }
> - return EXIT_SUCCESS;
> + return ksft_exit_pass();
> }
^ permalink raw reply
* Re: [PATCHv7 05/26] vfio: introduce the VFIO_DMA_MAP_FLAG_NOEXEC flag
From: Alex Williamson @ 2014-09-23 22:30 UTC (permalink / raw)
To: Antonios Motakis
Cc: kvm-u79uwXL29TY76Z2rM5mHXA, eric.auger-QSEj5FYQhm4dnm+yROfE0A,
marc.zyngier-5wv7dgnIgG8, open list:ABI/API,
will.deacon-5wv7dgnIgG8, open list,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
tech-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J,
kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg,
christoffer.dall-QSEj5FYQhm4dnm+yROfE0A
In-Reply-To: <1411510876.24563.47.camel-85EaTFmN5p//9pzu0YdTqQ@public.gmane.org>
On Tue, 2014-09-23 at 16:21 -0600, Alex Williamson wrote:
> On Tue, 2014-09-23 at 16:46 +0200, Antonios Motakis wrote:
> > We introduce the VFIO_DMA_MAP_FLAG_NOEXEC flag to the VFIO dma map call,
> > and expose its availability via the capability VFIO_IOMMU_PROT_NOEXEC.
> > This way the user can control whether the XN flag will be set on the
> > requested mappings. The IOMMU_NOEXEC flag needs to be available for all
> > the IOMMUs of the container used.
> >
> > Signed-off-by: Antonios Motakis <a.motakis-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J@public.gmane.org>
> > ---
> > include/uapi/linux/vfio.h | 2 ++
> > 1 file changed, 2 insertions(+)
> >
> > diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
> > index 6612974..30f630c 100644
> > --- a/include/uapi/linux/vfio.h
> > +++ b/include/uapi/linux/vfio.h
> > @@ -29,6 +29,7 @@
> > * capability is subject to change as groups are added or removed.
> > */
> > #define VFIO_DMA_CC_IOMMU 4
> > +#define VFIO_IOMMU_PROT_NOEXEC 5
>
> Can't we advertise this as a flag bit in vfio_iommu_type1_info instead?
Ok, I see in the next patch that it's pretty similar to
VFIO_DMA_CC_IOMMU, so the check extension is probably correct for
determining the current state. Maybe we could name it more similarly,
VFIO_DMA_NOEXEC_IOMMU. I guess the intended usage is that once a user
attaches a group to the container they can query whether the
VFIO_DMA_MAP_FLAG_NOEXEC is valid. Ok. Thanks,
Alex
> Also, EEH already took 5 as seen immediately below.
>
> >
> > /* Check if EEH is supported */
> > #define VFIO_EEH 5
> > @@ -401,6 +402,7 @@ struct vfio_iommu_type1_dma_map {
> > __u32 flags;
> > #define VFIO_DMA_MAP_FLAG_READ (1 << 0) /* readable from device */
> > #define VFIO_DMA_MAP_FLAG_WRITE (1 << 1) /* writable from device */
> > +#define VFIO_DMA_MAP_FLAG_NOEXEC (1 << 2) /* not executable from device */
> > __u64 vaddr; /* Process virtual address */
> > __u64 iova; /* IO virtual address */
> > __u64 size; /* Size of mapping (bytes) */
>
>
^ permalink raw reply
* Re: [PATCHv7 05/26] vfio: introduce the VFIO_DMA_MAP_FLAG_NOEXEC flag
From: Alex Williamson @ 2014-09-23 22:21 UTC (permalink / raw)
To: Antonios Motakis
Cc: kvm-u79uwXL29TY76Z2rM5mHXA, eric.auger-QSEj5FYQhm4dnm+yROfE0A,
marc.zyngier-5wv7dgnIgG8, open list:ABI/API,
will.deacon-5wv7dgnIgG8, open list,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
tech-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J,
kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg,
christoffer.dall-QSEj5FYQhm4dnm+yROfE0A
In-Reply-To: <1411483586-29304-6-git-send-email-a.motakis-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J@public.gmane.org>
On Tue, 2014-09-23 at 16:46 +0200, Antonios Motakis wrote:
> We introduce the VFIO_DMA_MAP_FLAG_NOEXEC flag to the VFIO dma map call,
> and expose its availability via the capability VFIO_IOMMU_PROT_NOEXEC.
> This way the user can control whether the XN flag will be set on the
> requested mappings. The IOMMU_NOEXEC flag needs to be available for all
> the IOMMUs of the container used.
>
> Signed-off-by: Antonios Motakis <a.motakis-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J@public.gmane.org>
> ---
> include/uapi/linux/vfio.h | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
> index 6612974..30f630c 100644
> --- a/include/uapi/linux/vfio.h
> +++ b/include/uapi/linux/vfio.h
> @@ -29,6 +29,7 @@
> * capability is subject to change as groups are added or removed.
> */
> #define VFIO_DMA_CC_IOMMU 4
> +#define VFIO_IOMMU_PROT_NOEXEC 5
Can't we advertise this as a flag bit in vfio_iommu_type1_info instead?
Also, EEH already took 5 as seen immediately below.
>
> /* Check if EEH is supported */
> #define VFIO_EEH 5
> @@ -401,6 +402,7 @@ struct vfio_iommu_type1_dma_map {
> __u32 flags;
> #define VFIO_DMA_MAP_FLAG_READ (1 << 0) /* readable from device */
> #define VFIO_DMA_MAP_FLAG_WRITE (1 << 1) /* writable from device */
> +#define VFIO_DMA_MAP_FLAG_NOEXEC (1 << 2) /* not executable from device */
> __u64 vaddr; /* Process virtual address */
> __u64 iova; /* IO virtual address */
> __u64 size; /* Size of mapping (bytes) */
^ permalink raw reply
* [PATCH v2 7/7] selftests/timers: change test to use ksft framework
From: Shuah Khan @ 2014-09-23 21:33 UTC (permalink / raw)
To: akpm, gregkh, colin.king, dbueso, ebiederm, serge.hallyn, thierry,
luto
Cc: Shuah Khan, linux-api, linux-kernel
In-Reply-To: <cover.1411506121.git.shuahkh@osg.samsung.com>
Change timers test to use kselftest framework to report
test results.
Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com>
---
tools/testing/selftests/timers/posix_timers.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/timers/posix_timers.c b/tools/testing/selftests/timers/posix_timers.c
index 41bd855..f87d970 100644
--- a/tools/testing/selftests/timers/posix_timers.c
+++ b/tools/testing/selftests/timers/posix_timers.c
@@ -15,6 +15,8 @@
#include <time.h>
#include <pthread.h>
+#include "../kselftest.h"
+
#define DELAY 2
#define USECS_PER_SEC 1000000
@@ -194,16 +196,16 @@ int main(int argc, char **argv)
printf("based timers if other threads run on the CPU...\n");
if (check_itimer(ITIMER_VIRTUAL) < 0)
- return -1;
+ return ksft_exit_fail();
if (check_itimer(ITIMER_PROF) < 0)
- return -1;
+ return ksft_exit_fail();
if (check_itimer(ITIMER_REAL) < 0)
- return -1;
+ return ksft_exit_fail();
if (check_timer_create(CLOCK_THREAD_CPUTIME_ID) < 0)
- return -1;
+ return ksft_exit_fail();
/*
* It's unfortunately hard to reliably test a timer expiration
@@ -215,7 +217,7 @@ int main(int argc, char **argv)
* find a better solution.
*/
if (check_timer_create(CLOCK_PROCESS_CPUTIME_ID) < 0)
- return -1;
+ return ksft_exit_fail();
- return 0;
+ return ksft_exit_pass();
}
--
1.9.1
^ permalink raw reply related
* [PATCH v2 6/7] selftests/ptrace: change test to use ksft framework
From: Shuah Khan @ 2014-09-23 21:32 UTC (permalink / raw)
To: akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
colin.king-Z7WLFzj8eWMS+FvcfC7Uqw, dbueso-l3A5Bk7waGM,
ebiederm-aS9lmoZGLiVWk0Htik3J/w,
serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA,
thierry-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
luto-kltTT9wpgjJwATOyAt5JVQ
Cc: Shuah Khan, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <cover.1411506121.git.shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
Change ptrace test to use kselftest framework to report
test results.
Signed-off-by: Shuah Khan <shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
---
tools/testing/selftests/ptrace/peeksiginfo.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/ptrace/peeksiginfo.c b/tools/testing/selftests/ptrace/peeksiginfo.c
index c34cd8a..d940bfc 100644
--- a/tools/testing/selftests/ptrace/peeksiginfo.c
+++ b/tools/testing/selftests/ptrace/peeksiginfo.c
@@ -10,6 +10,7 @@
#include <sys/mman.h>
#include "linux/ptrace.h"
+#include "../kselftest.h"
static int sys_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *uinfo)
{
@@ -151,7 +152,7 @@ out:
int main(int argc, char *argv[])
{
siginfo_t siginfo[SIGNR];
- int i, exit_code = 1;
+ int i;
sigset_t blockmask;
pid_t child;
@@ -162,7 +163,7 @@ int main(int argc, char *argv[])
child = fork();
if (child == -1) {
err("fork() failed: %m");
- return 1;
+ return ksft_exit_fail();
} else if (child == 0) {
pid_t ppid = getppid();
while (1) {
@@ -170,7 +171,7 @@ int main(int argc, char *argv[])
break;
sleep(1);
}
- return 1;
+ return ksft_exit_fail();
}
/* Send signals in process-wide and per-thread queues */
@@ -185,7 +186,7 @@ int main(int argc, char *argv[])
}
if (sys_ptrace(PTRACE_ATTACH, child, NULL, NULL) == -1)
- return 1;
+ return ksft_exit_fail();
waitpid(child, NULL, 0);
@@ -207,12 +208,11 @@ int main(int argc, char *argv[])
goto out;
printf("PASS\n");
- exit_code = 0;
out:
if (sys_ptrace(PTRACE_KILL, child, NULL, NULL) == -1)
- return 1;
+ return ksft_exit_fail();
waitpid(child, NULL, 0);
- return exit_code;
+ return ksft_exit_pass();
}
--
1.9.1
^ permalink raw reply related
* [PATCH v2 5/7] selftests/mount: change test to use ksft framework
From: Shuah Khan @ 2014-09-23 21:32 UTC (permalink / raw)
To: akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
colin.king-Z7WLFzj8eWMS+FvcfC7Uqw, dbueso-l3A5Bk7waGM,
ebiederm-aS9lmoZGLiVWk0Htik3J/w,
serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA,
thierry-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
luto-kltTT9wpgjJwATOyAt5JVQ
Cc: Shuah Khan, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <cover.1411506121.git.shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
Change mount test to use kselftest framework to report
test results.
Signed-off-by: Shuah Khan <shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
---
tools/testing/selftests/mount/unprivileged-remount-test.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/mount/unprivileged-remount-test.c b/tools/testing/selftests/mount/unprivileged-remount-test.c
index 1b3ff2f..c64d442 100644
--- a/tools/testing/selftests/mount/unprivileged-remount-test.c
+++ b/tools/testing/selftests/mount/unprivileged-remount-test.c
@@ -13,6 +13,8 @@
#include <stdbool.h>
#include <stdarg.h>
+#include "../kselftest.h"
+
#ifndef CLONE_NEWNS
# define CLONE_NEWNS 0x00020000
#endif
@@ -45,7 +47,7 @@ static void die(char *fmt, ...)
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
- exit(EXIT_FAILURE);
+ ksft_exit_fail();
}
static void write_file(char *filename, char *fmt, ...)
@@ -176,7 +178,7 @@ bool test_unpriv_remount(int mount_flags, int remount_flags, int invalid_flags)
die("remount of /tmp with invalid flags "
"succeeded unexpectedly\n");
}
- exit(EXIT_SUCCESS);
+ return ksft_exit_pass();
}
static bool test_unpriv_remount_simple(int mount_flags)
@@ -238,5 +240,5 @@ int main(int argc, char **argv)
{
die("Default atime malfunctions\n");
}
- return EXIT_SUCCESS;
+ return ksft_exit_pass();
}
--
1.9.1
^ permalink raw reply related
* [PATCH v2 4/7] selftests/kcmp: change test to use ksft framework
From: Shuah Khan @ 2014-09-23 21:32 UTC (permalink / raw)
To: akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
colin.king-Z7WLFzj8eWMS+FvcfC7Uqw, dbueso-l3A5Bk7waGM,
ebiederm-aS9lmoZGLiVWk0Htik3J/w,
serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA,
thierry-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
luto-kltTT9wpgjJwATOyAt5JVQ
Cc: Shuah Khan, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <cover.1411506121.git.shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
Change kcmp test to use kselftest framework to report
test results and test statistics.
Signed-off-by: Shuah Khan <shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
---
tools/testing/selftests/kcmp/kcmp_test.c | 27 ++++++++++++++++++++-------
1 file changed, 20 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/kcmp/kcmp_test.c b/tools/testing/selftests/kcmp/kcmp_test.c
index dbba408..a5a4da8 100644
--- a/tools/testing/selftests/kcmp/kcmp_test.c
+++ b/tools/testing/selftests/kcmp/kcmp_test.c
@@ -17,6 +17,8 @@
#include <sys/stat.h>
#include <sys/wait.h>
+#include "../kselftest.h"
+
static long sys_kcmp(int pid1, int pid2, int type, int fd1, int fd2)
{
return syscall(__NR_kcmp, pid1, pid2, type, fd1, fd2);
@@ -34,13 +36,13 @@ int main(int argc, char **argv)
if (fd1 < 0) {
perror("Can't create file");
- exit(1);
+ ksft_exit_fail();
}
pid2 = fork();
if (pid2 < 0) {
perror("fork failed");
- exit(1);
+ ksft_exit_fail();
}
if (!pid2) {
@@ -50,7 +52,7 @@ int main(int argc, char **argv)
fd2 = open(kpath, O_RDWR, 0644);
if (fd2 < 0) {
perror("Can't open file");
- exit(1);
+ ksft_exit_fail();
}
/* An example of output and arguments */
@@ -74,23 +76,34 @@ int main(int argc, char **argv)
if (ret) {
printf("FAIL: 0 expected but %d returned (%s)\n",
ret, strerror(errno));
+ ksft_inc_fail_cnt();
ret = -1;
- } else
+ } else {
printf("PASS: 0 returned as expected\n");
+ ksft_inc_pass_cnt();
+ }
/* Compare with self */
ret = sys_kcmp(pid1, pid1, KCMP_VM, 0, 0);
if (ret) {
printf("FAIL: 0 expected but %d returned (%s)\n",
ret, strerror(errno));
+ ksft_inc_fail_cnt();
ret = -1;
- } else
+ } else {
printf("PASS: 0 returned as expected\n");
+ ksft_inc_pass_cnt();
+ }
+
+ ksft_print_cnts();
- exit(ret);
+ if (ret)
+ ksft_exit_fail();
+ else
+ ksft_exit_pass();
}
waitpid(pid2, &status, P_ALL);
- return 0;
+ return ksft_exit_pass();
}
--
1.9.1
^ permalink raw reply related
* [PATCH v2 3/7] selftests/ipc: change test to use ksft framework
From: Shuah Khan @ 2014-09-23 21:32 UTC (permalink / raw)
To: akpm, gregkh, colin.king, dbueso, ebiederm, serge.hallyn, thierry,
luto
Cc: Shuah Khan, linux-api, linux-kernel
In-Reply-To: <cover.1411506121.git.shuahkh@osg.samsung.com>
Change ipc test to use kselftest framework to report
test results. With this change this test exits with
EXIT_FAIL instead of -errno. Changed print errno in
test fail messages to not loose that information.
Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com>
---
tools/testing/selftests/ipc/msgque.c | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/tools/testing/selftests/ipc/msgque.c b/tools/testing/selftests/ipc/msgque.c
index 552f081..1b2ce33 100644
--- a/tools/testing/selftests/ipc/msgque.c
+++ b/tools/testing/selftests/ipc/msgque.c
@@ -5,6 +5,8 @@
#include <linux/msg.h>
#include <fcntl.h>
+#include "../kselftest.h"
+
#define MAX_MSG_SIZE 32
struct msg1 {
@@ -195,58 +197,58 @@ int main(int argc, char **argv)
if (getuid() != 0) {
printf("Please run the test as root - Exiting.\n");
- exit(1);
+ return ksft_exit_fail();
}
msgque.key = ftok(argv[0], 822155650);
if (msgque.key == -1) {
- printf("Can't make key\n");
- return -errno;
+ printf("Can't make key: %d\n", -errno);
+ return ksft_exit_fail();
}
msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666);
if (msgque.msq_id == -1) {
err = -errno;
- printf("Can't create queue\n");
+ printf("Can't create queue: %d\n", err);
goto err_out;
}
err = fill_msgque(&msgque);
if (err) {
- printf("Failed to fill queue\n");
+ printf("Failed to fill queue: %d\n", err);
goto err_destroy;
}
err = dump_queue(&msgque);
if (err) {
- printf("Failed to dump queue\n");
+ printf("Failed to dump queue: %d\n", err);
goto err_destroy;
}
err = check_and_destroy_queue(&msgque);
if (err) {
- printf("Failed to check and destroy queue\n");
+ printf("Failed to check and destroy queue: %d\n", err);
goto err_out;
}
err = restore_queue(&msgque);
if (err) {
- printf("Failed to restore queue\n");
+ printf("Failed to restore queue: %d\n", err);
goto err_destroy;
}
err = check_and_destroy_queue(&msgque);
if (err) {
- printf("Failed to test queue\n");
+ printf("Failed to test queue: %d\n", err);
goto err_out;
}
- return 0;
+ return ksft_exit_pass();
err_destroy:
if (msgctl(msgque.msq_id, IPC_RMID, 0)) {
printf("Failed to destroy queue: %d\n", -errno);
- return -errno;
+ return ksft_exit_fail();
}
err_out:
- return err;
+ return ksft_exit_fail();
}
--
1.9.1
^ permalink raw reply related
* [PATCH v2 2/7] selftests/breakpoints: change test to use ksft framework
From: Shuah Khan @ 2014-09-23 21:32 UTC (permalink / raw)
To: akpm, gregkh, colin.king, dbueso, ebiederm, serge.hallyn, thierry,
luto
Cc: Shuah Khan, linux-api, linux-kernel
In-Reply-To: <cover.1411506121.git.shuahkh@osg.samsung.com>
Change breakpoints test to use kselftest framework to report
test results.
Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com>
---
tools/testing/selftests/breakpoints/breakpoint_test.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/breakpoints/breakpoint_test.c b/tools/testing/selftests/breakpoints/breakpoint_test.c
index a0743f3..120895a 100644
--- a/tools/testing/selftests/breakpoints/breakpoint_test.c
+++ b/tools/testing/selftests/breakpoints/breakpoint_test.c
@@ -17,6 +17,8 @@
#include <sys/types.h>
#include <sys/wait.h>
+#include "../kselftest.h"
+
/* Breakpoint access modes */
enum {
@@ -42,7 +44,7 @@ static void set_breakpoint_addr(void *addr, int n)
offsetof(struct user, u_debugreg[n]), addr);
if (ret) {
perror("Can't set breakpoint addr\n");
- exit(-1);
+ ksft_exit_fail();
}
}
@@ -105,7 +107,7 @@ static void toggle_breakpoint(int n, int type, int len,
offsetof(struct user, u_debugreg[7]), dr7);
if (ret) {
perror("Can't set dr7");
- exit(-1);
+ ksft_exit_fail();
}
}
@@ -275,7 +277,7 @@ static void check_success(const char *msg)
msg2 = "Ok";
if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1)) {
perror("Can't poke\n");
- exit(-1);
+ ksft_exit_fail();
}
}
@@ -390,5 +392,5 @@ int main(int argc, char **argv)
wait(NULL);
- return 0;
+ return ksft_exit_pass();
}
--
1.9.1
^ permalink raw reply related
* [PATCH v2 1/7] selftests: add kselftest framework for uniform test reporting
From: Shuah Khan @ 2014-09-23 21:32 UTC (permalink / raw)
To: akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
colin.king-Z7WLFzj8eWMS+FvcfC7Uqw, dbueso-l3A5Bk7waGM,
ebiederm-aS9lmoZGLiVWk0Htik3J/w,
serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA,
thierry-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
luto-kltTT9wpgjJwATOyAt5JVQ
Cc: Shuah Khan, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <cover.1411506121.git.shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
Add kselftest framework for tests to use. This is a light
weight framework provides a set of interfaces to report test
results. Tests can use these interfaces to report pass, and
fail cases as well as when failure is due to configuration
problems such as missing modules, or when a test that is should
fail, fails as expected, and a test that should fail, passes.
The framework uses POSIX standard return codes for reporting
results to address the needs of users that want to run the kernel
selftests from their user-space test suites and want to know why a
test failed. In addition, the framework includes interfaces to use
to report test statistics on number of tests passed and failed.
Signed-off-by: Shuah Khan <shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
---
tools/testing/selftests/kselftest.h | 41 +++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 tools/testing/selftests/kselftest.h
diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h
new file mode 100644
index 0000000..c73fc2c
--- /dev/null
+++ b/tools/testing/selftests/kselftest.h
@@ -0,0 +1,41 @@
+/*
+ * kselftest.h: kselftest framework return codes to include from
+ * selftests.
+ *
+ * Copyright (c) 2014 Shuah Khan <shuahkh-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * This file is released under the GPLv2.
+ */
+#ifndef __KSELFTEST_H
+#define __KSELFTEST_H
+
+#include <stdlib.h>
+#include <unistd.h>
+
+/* counters */
+static int ksft_pass;
+static int ksft_fail;
+static int ksft_xfail;
+static int ksft_xpass;
+static int ksft_xskip;
+
+static inline void ksft_inc_pass_cnt(void) { ksft_pass++; }
+static inline void ksft_inc_fail_cnt(void) { ksft_fail++; }
+static inline void ksft_inc_xfail_cnt(void) { ksft_xfail++; }
+static inline void ksft_inc_xpass_cnt(void) { ksft_xpass++; }
+static inline void ksft_inc_xskip_cnt(void) { ksft_xskip++; }
+
+static inline void ksft_print_cnts(void)
+{
+ printf("Pass: %d Fail: %d Xfail: %d Xpass: %d, Xskip: %d\n",
+ ksft_pass, ksft_fail, ksft_xfail, ksft_xpass, ksft_xskip);
+}
+
+static inline int ksft_exit_pass(void) { exit(0); }
+static inline int ksft_exit_fail(void) { exit(1); }
+static inline int ksft_exit_xfail(void) { exit(2); }
+static inline int ksft_exit_xpass(void) { exit(3); }
+static inline int ksft_exit_skip(void) { exit(4); }
+
+#endif /* __KSELFTEST_H */
--
1.9.1
^ permalink raw reply related
* [PATCH v2 0/7] kselftest framework and test changes to use it
From: Shuah Khan @ 2014-09-23 21:32 UTC (permalink / raw)
To: akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
colin.king-Z7WLFzj8eWMS+FvcfC7Uqw, dbueso-l3A5Bk7waGM,
ebiederm-aS9lmoZGLiVWk0Htik3J/w,
serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA,
thierry-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8,
luto-kltTT9wpgjJwATOyAt5JVQ
Cc: Shuah Khan, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
This patch v2 series: (revised to address v1 comments)
Add kselftest framework for tests to use. This is a light
weight framework provides a set of interfaces to report test
results and test statistics on number of tests passed and failed.
Several tests are changed to use the framework to report results.
Shuah Khan (7):
selftests: add kselftest framework for uniform test reporting
selftests/breakpoints: change test to use ksft framework
selftests/ipc: change test to use ksft framework
selftests/kcmp: change test to use ksft framework
selftests/mount: change test to use ksft framework
selftests/ptrace: change test to use ksft framework
selftests/timers: change test to use ksft framework
.../selftests/breakpoints/breakpoint_test.c | 10 +++---
tools/testing/selftests/ipc/msgque.c | 26 +++++++-------
tools/testing/selftests/kcmp/kcmp_test.c | 27 ++++++++++----
tools/testing/selftests/kselftest.h | 41 ++++++++++++++++++++++
.../selftests/mount/unprivileged-remount-test.c | 8 +++--
tools/testing/selftests/ptrace/peeksiginfo.c | 14 ++++----
tools/testing/selftests/timers/posix_timers.c | 14 ++++----
7 files changed, 101 insertions(+), 39 deletions(-)
create mode 100644 tools/testing/selftests/kselftest.h
--
1.9.1
^ permalink raw reply
* [PATCH v2 3/3] tools: parallel selftests building & running
From: Peter Feiner @ 2014-09-23 19:55 UTC (permalink / raw)
To: Shuah Khan
Cc: linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, David Herrmann, Greg Thelen,
Hugh Dickens, Andrew Morton, Peter Feiner
In-Reply-To: <1411502120-28219-1-git-send-email-pfeiner-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Now make -jN builds and runs selftests in parallel. Also, if one
selftest fails to build or run, make will return an error, whereas
before the error was ignored.
Signed-off-by: Peter Feiner <pfeiner-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
---
v1 -> v2:
Moved fix for missing 'make clean' target into separate
patch.
---
tools/testing/selftests/Makefile | 49 ++++++++++++++++------------------------
1 file changed, 20 insertions(+), 29 deletions(-)
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 36ff2e4..8c33716 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -18,32 +18,23 @@ TARGETS += firmware
TARGETS_HOTPLUG = cpu-hotplug
TARGETS_HOTPLUG += memory-hotplug
-all:
- for TARGET in $(TARGETS); do \
- make -C $$TARGET; \
- done;
-
-run_tests: all
- for TARGET in $(TARGETS); do \
- make -C $$TARGET run_tests; \
- done;
-
-hotplug:
- for TARGET in $(TARGETS_HOTPLUG); do \
- make -C $$TARGET; \
- done;
-
-run_hotplug: hotplug
- for TARGET in $(TARGETS_HOTPLUG); do \
- make -C $$TARGET run_full_test; \
- done;
-
-clean_hotplug:
- for TARGET in $(TARGETS_HOTPLUG); do \
- make -C $$TARGET clean; \
- done;
-
-clean:
- for TARGET in $(TARGETS); do \
- make -C $$TARGET clean; \
- done;
+BUILD_TARGETS=$(TARGETS:%=build-%) $(TARGETS_HOTPLUG:%=build-%)
+TEST_TARGETS=$(TARGETS:%=test-%)
+CLEAN_TARGETS=$(TARGETS:%=clean-%) $(TARGETS_HOTPLUG:%=clean-%)
+
+all: $(BUILD_TARGETS)
+
+run_hotplug: test-cpu-hotplug test-memory-hotplug
+
+run_tests: $(TEST_TARGETS)
+
+clean: $(CLEAN_TARGETS)
+
+build-%:
+ $(MAKE) -C $(@:build-%=%)
+
+test-%: build-%
+ $(MAKE) -C $(@:test-%=%) run_tests
+
+clean-%:
+ $(MAKE) -C $(@:clean-%=%) clean
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox