Linux userland API discussions
 help / color / mirror / Atom feed
* [PATCH 3/4] Adds project quota support for ext4
From: Li Xi @ 2014-09-24 14:04 UTC (permalink / raw)
  To: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
	hch, dmonakhov
In-Reply-To: <1411567470-31799-1-git-send-email-lixi@ddn.com>

This patch adds mount options for enabling/disabling project quota
accounting and enforcement. A new specific inode is also used for
project quota accounting.

Signed-off-by: Li Xi <lixi@ddn.com>
Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
---
 fs/ext4/ext4.h  |    8 +++-
 fs/ext4/super.c |  123 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 118 insertions(+), 13 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 092f60c..f8be9bf 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -217,6 +217,7 @@ struct ext4_io_submit {
 #define EXT4_UNDEL_DIR_INO	 6	/* Undelete directory inode */
 #define EXT4_RESIZE_INO		 7	/* Reserved group descriptors inode */
 #define EXT4_JOURNAL_INO	 8	/* Journal inode */
+#define EXT4_PRJ_QUOTA_INO	 9	/* Project quota inode */
 
 /* First non-reserved inode for old ext4 filesystems */
 #define EXT4_GOOD_OLD_FIRST_INO	11
@@ -991,6 +992,7 @@ struct ext4_inode_info {
 #define EXT4_MOUNT_DIOREAD_NOLOCK	0x400000 /* Enable support for dio read nolocking */
 #define EXT4_MOUNT_JOURNAL_CHECKSUM	0x800000 /* Journal checksums */
 #define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT	0x1000000 /* Journal Async Commit */
+#define EXT4_MOUNT_PRJQUOTA		0x2000000 /* Project quota support */
 #define EXT4_MOUNT_DELALLOC		0x8000000 /* Delalloc support */
 #define EXT4_MOUNT_DATA_ERR_ABORT	0x10000000 /* Abort on file data write */
 #define EXT4_MOUNT_BLOCK_VALIDITY	0x20000000 /* Block validity checking */
@@ -1166,7 +1168,8 @@ struct ext4_super_block {
 	__le32	s_grp_quota_inum;	/* inode for tracking group quota */
 	__le32	s_overhead_clusters;	/* overhead blocks/clusters in fs */
 	__le32	s_backup_bgs[2];	/* groups with sparse_super2 SBs */
-	__le32	s_reserved[106];	/* Padding to the end of the block */
+	__le32	s_prj_quota_inum;	/* inode for tracking project quota */
+	__le32	s_reserved[105];	/* Padding to the end of the block */
 	__le32	s_checksum;		/* crc32c(superblock) */
 };
 
@@ -1181,7 +1184,7 @@ struct ext4_super_block {
 #define EXT4_MF_FS_ABORTED	0x0002	/* Fatal error detected */
 
 /* Number of quota types we support */
-#define EXT4_MAXQUOTAS 2
+#define EXT4_MAXQUOTAS 3
 
 /*
  * fourth extended-fs super-block data in memory
@@ -1372,6 +1375,7 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
 		ino == EXT4_BOOT_LOADER_INO ||
 		ino == EXT4_JOURNAL_INO ||
 		ino == EXT4_RESIZE_INO ||
+		ino == EXT4_PRJ_QUOTA_INO ||
 		(ino >= EXT4_FIRST_INO(sb) &&
 		 ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count));
 }
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 8c3e87b..5cb5055 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1048,8 +1048,8 @@ static int bdev_try_to_free_page(struct super_block *sb, struct page *page,
 }
 
 #ifdef CONFIG_QUOTA
-#define QTYPE2NAME(t) ((t) == USRQUOTA ? "user" : "group")
-#define QTYPE2MOPT(on, t) ((t) == USRQUOTA?((on)##USRJQUOTA):((on)##GRPJQUOTA))
+static char *quotatypes[] = INITQFNAMES;
+#define QTYPE2NAME(t) (quotatypes[t])
 
 static int ext4_write_dquot(struct dquot *dquot);
 static int ext4_acquire_dquot(struct dquot *dquot);
@@ -1160,10 +1160,11 @@ enum {
 	Opt_journal_path, Opt_journal_checksum, Opt_journal_async_commit,
 	Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
 	Opt_data_err_abort, Opt_data_err_ignore,
-	Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
+	Opt_usrjquota, Opt_grpjquota, Opt_prjjquota,
+	Opt_offusrjquota, Opt_offgrpjquota, Opt_offprjjquota,
 	Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
 	Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
-	Opt_usrquota, Opt_grpquota, Opt_i_version,
+	Opt_usrquota, Opt_grpquota, Opt_prjquota, Opt_i_version,
 	Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_mblk_io_submit,
 	Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity,
 	Opt_inode_readahead_blks, Opt_journal_ioprio,
@@ -1214,6 +1215,8 @@ static const match_table_t tokens = {
 	{Opt_usrjquota, "usrjquota=%s"},
 	{Opt_offgrpjquota, "grpjquota="},
 	{Opt_grpjquota, "grpjquota=%s"},
+	{Opt_offprjjquota, "prjjquota="},
+	{Opt_prjjquota, "prjjquota=%s"},
 	{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
 	{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
 	{Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
@@ -1221,6 +1224,7 @@ static const match_table_t tokens = {
 	{Opt_noquota, "noquota"},
 	{Opt_quota, "quota"},
 	{Opt_usrquota, "usrquota"},
+	{Opt_prjquota, "prjquota"},
 	{Opt_barrier, "barrier=%u"},
 	{Opt_barrier, "barrier"},
 	{Opt_nobarrier, "nobarrier"},
@@ -1433,6 +1437,8 @@ static const struct mount_opts {
 							MOPT_SET | MOPT_Q},
 	{Opt_grpquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_GRPQUOTA,
 							MOPT_SET | MOPT_Q},
+	{Opt_prjquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_PRJQUOTA,
+							MOPT_SET | MOPT_Q},
 	{Opt_noquota, (EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
 		       EXT4_MOUNT_GRPQUOTA), MOPT_CLEAR | MOPT_Q},
 	{Opt_usrjquota, 0, MOPT_Q},
@@ -1461,10 +1467,14 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
 		return set_qf_name(sb, USRQUOTA, &args[0]);
 	else if (token == Opt_grpjquota)
 		return set_qf_name(sb, GRPQUOTA, &args[0]);
+	else if (token == Opt_prjjquota)
+		return set_qf_name(sb, PRJQUOTA, &args[0]);
 	else if (token == Opt_offusrjquota)
 		return clear_qf_name(sb, USRQUOTA);
 	else if (token == Opt_offgrpjquota)
 		return clear_qf_name(sb, GRPQUOTA);
+	else if (token == Opt_offprjjquota)
+		return clear_qf_name(sb, PRJQUOTA);
 #endif
 	switch (token) {
 	case Opt_noacl:
@@ -1690,19 +1700,28 @@ static int parse_options(char *options, struct super_block *sb,
 	}
 #ifdef CONFIG_QUOTA
 	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
-	    (test_opt(sb, USRQUOTA) || test_opt(sb, GRPQUOTA))) {
+	    (test_opt(sb, USRQUOTA) ||
+	     test_opt(sb, GRPQUOTA) ||
+	     test_opt(sb, PRJQUOTA))) {
 		ext4_msg(sb, KERN_ERR, "Cannot set quota options when QUOTA "
 			 "feature is enabled");
 		return 0;
 	}
-	if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
+	if (sbi->s_qf_names[USRQUOTA] ||
+	    sbi->s_qf_names[GRPQUOTA] ||
+	    sbi->s_qf_names[PRJQUOTA]) {
 		if (test_opt(sb, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
 			clear_opt(sb, USRQUOTA);
 
 		if (test_opt(sb, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
 			clear_opt(sb, GRPQUOTA);
 
-		if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
+		if (test_opt(sb, PRJQUOTA) && sbi->s_qf_names[PRJQUOTA])
+			clear_opt(sb, PRJQUOTA);
+
+		if (test_opt(sb, GRPQUOTA) ||
+		    test_opt(sb, USRQUOTA) ||
+		    test_opt(sb, PRJQUOTA)) {
 			ext4_msg(sb, KERN_ERR, "old and new quota "
 					"format mixing");
 			return 0;
@@ -1763,6 +1782,9 @@ static inline void ext4_show_quota_options(struct seq_file *seq,
 
 	if (sbi->s_qf_names[GRPQUOTA])
 		seq_printf(seq, ",grpjquota=%s", sbi->s_qf_names[GRPQUOTA]);
+
+	if (sbi->s_qf_names[PRJQUOTA])
+		seq_printf(seq, ",prjjquota=%s", sbi->s_qf_names[PRJQUOTA]);
 #endif
 }
 
@@ -2833,6 +2855,13 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
 			 "without CONFIG_QUOTA");
 		return 0;
 	}
+	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT) &&
+	    !readonly) {
+		ext4_msg(sb, KERN_ERR,
+			 "Filesystem with project quota feature cannot be"
+			 "mounted RDWR without CONFIG_QUOTA");
+		return 0;
+	}
 #endif  /* CONFIG_QUOTA */
 	return 1;
 }
@@ -5052,6 +5081,46 @@ restore_opts:
 	return err;
 }
 
+static int ext4_statfs_project(struct super_block *sb,
+			       kprojid_t projid, struct kstatfs *buf)
+{
+	struct kqid qid;
+	struct dquot *dquot;
+	u64 limit;
+	u64 curblock;
+
+	qid = make_kqid_projid(projid);
+	dquot = dqget(sb, qid);
+	if (!dquot)
+		return -ESRCH;
+	spin_lock(&dq_data_lock);
+
+	limit = dquot->dq_dqb.dqb_bsoftlimit ?
+		dquot->dq_dqb.dqb_bsoftlimit :
+		dquot->dq_dqb.dqb_bhardlimit;
+	if (limit && buf->f_blocks * buf->f_bsize > limit) {
+		curblock = dquot->dq_dqb.dqb_curspace / buf->f_bsize;
+		buf->f_blocks = limit / buf->f_bsize;
+		buf->f_bfree = buf->f_bavail =
+			(buf->f_blocks > curblock) ?
+			 (buf->f_blocks - curblock) : 0;
+	}
+
+	limit = dquot->dq_dqb.dqb_isoftlimit ?
+		dquot->dq_dqb.dqb_isoftlimit :
+		dquot->dq_dqb.dqb_ihardlimit;
+	if (limit && buf->f_files > limit) {
+		buf->f_files = limit;
+		buf->f_ffree =
+			(buf->f_files > dquot->dq_dqb.dqb_curinodes) ?
+			 (buf->f_ffree - dquot->dq_dqb.dqb_curinodes) : 0;
+	}
+
+	spin_unlock(&dq_data_lock);
+	dqput(dquot);
+	return 0;
+}
+
 static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
 	struct super_block *sb = dentry->d_sb;
@@ -5060,6 +5129,8 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
 	ext4_fsblk_t overhead = 0, resv_blocks;
 	u64 fsid;
 	s64 bfree;
+	struct inode *inode = dentry->d_inode;
+	int err = 0;
 	resv_blocks = EXT4_C2B(sbi, atomic64_read(&sbi->s_resv_clusters));
 
 	if (!test_opt(sb, MINIX_DF))
@@ -5084,7 +5155,18 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
 	buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL;
 	buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL;
 
-	return 0;
+	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT) &&
+	    ext4_test_inode_flag(inode, EXT4_INODE_PROJINHERIT) &&
+	    sb_has_quota_usage_enabled(sb, PRJQUOTA) &&
+	    sb_has_quota_limits_enabled(sb, PRJQUOTA)) {
+	    	err = ext4_statfs_project(sb, EXT4_I(inode)->i_projid, buf);
+	    	if (err) {
+			ext4_warning(sb, "Cannot get quota of project %u\n",
+				     from_kprojid(&init_user_ns,
+						  EXT4_I(inode)->i_projid));
+	    	}
+	}
+	return err;
 }
 
 /* Helper function for writing quotas on sync - we need to start transaction
@@ -5164,7 +5246,9 @@ static int ext4_mark_dquot_dirty(struct dquot *dquot)
 
 	/* Are we journaling quotas? */
 	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) ||
-	    sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
+	    sbi->s_qf_names[USRQUOTA] ||
+	    sbi->s_qf_names[GRPQUOTA] ||
+	    sbi->s_qf_names[PRJQUOTA]) {
 		dquot_mark_dquot_dirty(dquot);
 		return ext4_write_dquot(dquot);
 	} else {
@@ -5248,10 +5332,14 @@ static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
 	struct inode *qf_inode;
 	unsigned long qf_inums[EXT4_MAXQUOTAS] = {
 		le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
-		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
+		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum),
+		le32_to_cpu(EXT4_SB(sb)->s_es->s_prj_quota_inum)
 	};
 
 	BUG_ON(!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA));
+	BUG_ON(type == PRJQUOTA &&
+	       (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+				EXT4_FEATURE_RO_COMPAT_PROJECT)));
 
 	if (!qf_inums[type])
 		return -EPERM;
@@ -5276,11 +5364,16 @@ static int ext4_enable_quotas(struct super_block *sb)
 	int type, err = 0;
 	unsigned long qf_inums[EXT4_MAXQUOTAS] = {
 		le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
-		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
+		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum),
+		le32_to_cpu(EXT4_SB(sb)->s_es->s_prj_quota_inum)
 	};
 
 	sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE;
 	for (type = 0; type < EXT4_MAXQUOTAS; type++) {
+		if (type == PRJQUOTA &&
+		    (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+				EXT4_FEATURE_RO_COMPAT_PROJECT)))
+			continue;
 		if (qf_inums[type]) {
 			err = ext4_quota_enable(sb, type, QFMT_VFS_V1,
 						DQUOT_USAGE_ENABLED);
@@ -5305,6 +5398,10 @@ static int ext4_quota_on_sysfile(struct super_block *sb, int type,
 	if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
 		return -EINVAL;
 
+	if (type == PRJQUOTA &&
+	    (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT)))
+		return -EINVAL;
+
 	/*
 	 * USAGE was enabled at mount time. Only need to enable LIMITS now.
 	 */
@@ -5345,6 +5442,10 @@ static int ext4_quota_off_sysfile(struct super_block *sb, int type)
 	if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
 		return -EINVAL;
 
+	if (type == PRJQUOTA &&
+	    (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT)))
+		return -EINVAL;
+
 	/* Disable only the limits. */
 	return dquot_disable(sb, type, DQUOT_LIMITS_ENABLED);
 }
-- 
1.7.1


^ permalink raw reply related

* [PATCH 2/4] Adds project ID support for ext4
From: Li Xi @ 2014-09-24 14:04 UTC (permalink / raw)
  To: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
	hch, dmonakhov
In-Reply-To: <1411567470-31799-1-git-send-email-lixi@ddn.com>

This patch adds a new internal field of ext4 inode to save project
identifier. Also a new flag EXT4_INODE_PROJINHERIT is added for
inheriting project ID from parent directory.

Signed-off-by: Li Xi <lixi@ddn.com>
---
 fs/ext4/ext4.h   |   21 +++++++++++++++++----
 fs/ext4/ialloc.c |    6 ++++++
 fs/ext4/inode.c  |   29 ++++++++++++++++++++++++++++-
 fs/ext4/namei.c  |   10 ++++++++++
 fs/ext4/super.c  |    1 +
 5 files changed, 62 insertions(+), 5 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 1eb5b7b..092f60c 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -386,16 +386,18 @@ struct flex_groups {
 #define EXT4_EA_INODE_FL	        0x00200000 /* Inode used for large EA */
 #define EXT4_EOFBLOCKS_FL		0x00400000 /* Blocks allocated beyond EOF */
 #define EXT4_INLINE_DATA_FL		0x10000000 /* Inode has inline data. */
+#define EXT4_PROJINHERIT_FL		0x20000000 /* Create with parents projid */
 #define EXT4_RESERVED_FL		0x80000000 /* reserved for ext4 lib */
 
-#define EXT4_FL_USER_VISIBLE		0x004BDFFF /* User visible flags */
-#define EXT4_FL_USER_MODIFIABLE		0x004380FF /* User modifiable flags */
+#define EXT4_FL_USER_VISIBLE		0x204BDFFF /* User visible flags */
+#define EXT4_FL_USER_MODIFIABLE		0x204380FF /* User modifiable flags */
 
 /* Flags that should be inherited by new inodes from their parent. */
 #define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\
 			   EXT4_SYNC_FL | EXT4_NODUMP_FL | EXT4_NOATIME_FL |\
 			   EXT4_NOCOMPR_FL | EXT4_JOURNAL_DATA_FL |\
-			   EXT4_NOTAIL_FL | EXT4_DIRSYNC_FL)
+			   EXT4_NOTAIL_FL | EXT4_DIRSYNC_FL |\
+			   EXT4_PROJINHERIT_FL)
 
 /* Flags that are appropriate for regular files (all but dir-specific ones). */
 #define EXT4_REG_FLMASK (~(EXT4_DIRSYNC_FL | EXT4_TOPDIR_FL))
@@ -443,6 +445,7 @@ enum {
 	EXT4_INODE_EA_INODE	= 21,	/* Inode used for large EA */
 	EXT4_INODE_EOFBLOCKS	= 22,	/* Blocks allocated beyond EOF */
 	EXT4_INODE_INLINE_DATA	= 28,	/* Data in inode. */
+	EXT4_INODE_PROJINHERIT	= 29,	/* Create with parents projid */
 	EXT4_INODE_RESERVED	= 31,	/* reserved for ext4 lib */
 };
 
@@ -694,6 +697,7 @@ struct ext4_inode {
 	__le32  i_crtime;       /* File Creation time */
 	__le32  i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */
 	__le32  i_version_hi;	/* high 32 bits for 64-bit version */
+	__le32  i_projid;	/* Project ID */
 };
 
 struct move_extent {
@@ -943,6 +947,7 @@ struct ext4_inode_info {
 
 	/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
 	__u32 i_csum_seed;
+	kprojid_t i_projid;
 };
 
 /*
@@ -1526,6 +1531,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
  * GDT_CSUM bits are mutually exclusive.
  */
 #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM	0x0400
+#define EXT4_FEATURE_RO_COMPAT_PROJECT		0x1000 /* Project quota */
 
 #define EXT4_FEATURE_INCOMPAT_COMPRESSION	0x0001
 #define EXT4_FEATURE_INCOMPAT_FILETYPE		0x0002
@@ -1575,7 +1581,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
 					 EXT4_FEATURE_RO_COMPAT_HUGE_FILE |\
 					 EXT4_FEATURE_RO_COMPAT_BIGALLOC |\
 					 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|\
-					 EXT4_FEATURE_RO_COMPAT_QUOTA)
+					 EXT4_FEATURE_RO_COMPAT_QUOTA |\
+					 EXT4_FEATURE_RO_COMPAT_PROJECT)
 
 /*
  * Default values for user and/or group using reserved blocks
@@ -1583,6 +1590,11 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
 #define	EXT4_DEF_RESUID		0
 #define	EXT4_DEF_RESGID		0
 
+/*
+ * Default project ID
+ */
+#define	EXT4_DEF_PROJID		0
+
 #define EXT4_DEF_INODE_READAHEAD_BLKS	32
 
 /*
@@ -2134,6 +2146,7 @@ extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
 			     loff_t lstart, loff_t lend);
 extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
 extern qsize_t *ext4_get_reserved_space(struct inode *inode);
+extern int ext4_get_projid(struct inode *inode, kprojid_t *projid);
 extern void ext4_da_update_reserve_space(struct inode *inode,
 					int used, int quota_claim);
 
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 5b87fc3..b6b7284 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -756,6 +756,12 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
 		inode->i_gid = dir->i_gid;
 	} else
 		inode_init_owner(inode, dir, mode);
+	if (ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT)) {
+		ei->i_projid = EXT4_I(dir)->i_projid;
+	} else {
+		ei->i_projid =
+			make_kprojid(&init_user_ns, EXT4_DEF_PROJID);
+	}
 	dquot_initialize(inode);
 
 	if (!goal)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index d5dd7d4..8c641eb 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3874,6 +3874,24 @@ static inline void ext4_iget_extra_inode(struct inode *inode,
 		EXT4_I(inode)->i_inline_off = 0;
 }
 
+static projid_t ext4_inode_projid_get(struct inode *inode, struct ext4_inode *raw,
+				 struct ext4_inode_info *ei)
+{
+	return (projid_t)le32_to_cpu(raw->i_projid);
+}
+
+static void ext4_inode_projid_set(struct inode *inode, struct ext4_inode *raw,
+				 struct ext4_inode_info *ei, projid_t projid)
+{
+	raw->i_projid = cpu_to_le32(projid);
+}
+
+int ext4_get_projid(struct inode *inode, kprojid_t *projid)
+{
+	*projid = EXT4_I(inode)->i_projid;
+	return 0;
+}
+
 struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 {
 	struct ext4_iloc iloc;
@@ -3885,6 +3903,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 	int block;
 	uid_t i_uid;
 	gid_t i_gid;
+	projid_t i_projid;
 
 	inode = iget_locked(sb, ino);
 	if (!inode)
@@ -3935,12 +3954,15 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 	inode->i_mode = le16_to_cpu(raw_inode->i_mode);
 	i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
 	i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
+	i_projid = ext4_inode_projid_get(inode, raw_inode, ei);
+
 	if (!(test_opt(inode->i_sb, NO_UID32))) {
 		i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
 		i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
 	}
 	i_uid_write(inode, i_uid);
 	i_gid_write(inode, i_gid);
+	ei->i_projid = make_kprojid(&init_user_ns, i_projid);;
 	set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
 
 	ext4_clear_state_flags(ei);	/* Only relevant on 32-bit archs */
@@ -4163,6 +4185,7 @@ static int ext4_do_update_inode(handle_t *handle,
 	int need_datasync = 0, set_large_file = 0;
 	uid_t i_uid;
 	gid_t i_gid;
+	projid_t i_projid;
 
 	spin_lock(&ei->i_raw_lock);
 
@@ -4175,6 +4198,7 @@ static int ext4_do_update_inode(handle_t *handle,
 	raw_inode->i_mode = cpu_to_le16(inode->i_mode);
 	i_uid = i_uid_read(inode);
 	i_gid = i_gid_read(inode);
+	i_projid = from_kprojid(&init_user_ns, ei->i_projid);
 	if (!(test_opt(inode->i_sb, NO_UID32))) {
 		raw_inode->i_uid_low = cpu_to_le16(low_16_bits(i_uid));
 		raw_inode->i_gid_low = cpu_to_le16(low_16_bits(i_gid));
@@ -4204,7 +4228,8 @@ static int ext4_do_update_inode(handle_t *handle,
 	EXT4_INODE_SET_XTIME(i_atime, inode, raw_inode);
 	EXT4_EINODE_SET_XTIME(i_crtime, ei, raw_inode);
 
-	if (ext4_inode_blocks_set(handle, raw_inode, ei)) {
+	err = ext4_inode_blocks_set(handle, raw_inode, ei);
+	if (err) {
 		spin_unlock(&ei->i_raw_lock);
 		goto out_brelse;
 	}
@@ -4255,6 +4280,8 @@ static int ext4_do_update_inode(handle_t *handle,
 
 	ext4_inode_csum_set(inode, raw_inode, ei);
 
+	ext4_inode_projid_set(inode, raw_inode, ei, i_projid);
+
 	spin_unlock(&ei->i_raw_lock);
 
 	BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 51705f8..1a9e557 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2944,6 +2944,11 @@ static int ext4_link(struct dentry *old_dentry,
 	if (inode->i_nlink >= EXT4_LINK_MAX)
 		return -EMLINK;
 
+	if ((ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT)) &&
+	    (!projid_eq(EXT4_I(dir)->i_projid,
+			EXT4_I(old_dentry->d_inode)->i_projid)))
+		return -EXDEV;
+
 	dquot_initialize(dir);
 
 retry:
@@ -3186,6 +3191,11 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 	int force_reread;
 	int retval;
 
+	if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT)) &&
+	    (!projid_eq(EXT4_I(new_dir)->i_projid,
+		EXT4_I(old_dentry->d_inode)->i_projid)))
+		return -EXDEV;
+
 	dquot_initialize(old.dir);
 	dquot_initialize(new.dir);
 
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 2632017..8c3e87b 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1080,6 +1080,7 @@ static const struct dquot_operations ext4_quota_operations = {
 	.write_info	= ext4_write_info,
 	.alloc_dquot	= dquot_alloc,
 	.destroy_dquot	= dquot_destroy,
+	.get_projid	= ext4_get_projid,
 };
 
 static const struct quotactl_ops ext4_qctl_operations = {
-- 
1.7.1


^ permalink raw reply related

* [PATCH 1/4] Adds general codes to enforces project quota limits
From: Li Xi @ 2014-09-24 14:04 UTC (permalink / raw)
  To: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
	hch, dmonakhov
In-Reply-To: <1411567470-31799-1-git-send-email-lixi@ddn.com>

This patch adds support for a new quota type PRJQUOTA for project quota
enforcement. Also a new method get_projid() is added into dquot_operations
structure.

Signed-off-by: Li Xi <lixi@ddn.com>
Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
---
 fs/quota/dquot.c           |   16 ++++++++++++++--
 fs/quota/quota.c           |    5 ++++-
 fs/quota/quotaio_v2.h      |    6 ++++--
 include/linux/quota.h      |    2 ++
 include/uapi/linux/quota.h |    6 ++++--
 5 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index f2d0eee..8d0c270 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -1154,8 +1154,8 @@ static int need_print_warning(struct dquot_warn *warn)
 			return uid_eq(current_fsuid(), warn->w_dq_id.uid);
 		case GRPQUOTA:
 			return in_group_p(warn->w_dq_id.gid);
-		case PRJQUOTA:	/* Never taken... Just make gcc happy */
-			return 0;
+		case PRJQUOTA:
+			return 1;
 	}
 	return 0;
 }
@@ -1394,6 +1394,9 @@ static void __dquot_initialize(struct inode *inode, int type)
 	/* First get references to structures we might need. */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		struct kqid qid;
+		kprojid_t projid;
+		int rc;
+
 		got[cnt] = NULL;
 		if (type != -1 && cnt != type)
 			continue;
@@ -1413,6 +1416,15 @@ static void __dquot_initialize(struct inode *inode, int type)
 		case GRPQUOTA:
 			qid = make_kqid_gid(inode->i_gid);
 			break;
+		case PRJQUOTA:
+			/* Project ID is not supported */
+			if (!inode->i_sb->dq_op->get_projid)
+				continue;
+			rc = inode->i_sb->dq_op->get_projid(inode, &projid);
+			if (rc)
+				continue;
+			qid = make_kqid_projid(projid);
+			break;
 		}
 		got[cnt] = dqget(sb, qid);
 	}
diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index 7562164..cce7371 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -30,7 +30,10 @@ static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
 	case Q_XGETQSTATV:
 	case Q_XQUOTASYNC:
 		break;
-	/* allow to query information for dquots we "own" */
+	/*
+	 * allow to query information for dquots we "own"
+	 * always allow quota check for project quota
+	 */
 	case Q_GETQUOTA:
 	case Q_XGETQUOTA:
 		if ((type == USRQUOTA && uid_eq(current_euid(), make_kuid(current_user_ns(), id))) ||
diff --git a/fs/quota/quotaio_v2.h b/fs/quota/quotaio_v2.h
index f1966b4..b17af05 100644
--- a/fs/quota/quotaio_v2.h
+++ b/fs/quota/quotaio_v2.h
@@ -13,12 +13,14 @@
  */
 #define V2_INITQMAGICS {\
 	0xd9c01f11,	/* USRQUOTA */\
-	0xd9c01927	/* GRPQUOTA */\
+	0xd9c01927,	/* GRPQUOTA */\
+	0xd9c03f14	/* PRJQUOTA */\
 }
 
 #define V2_INITQVERSIONS {\
 	1,		/* USRQUOTA */\
-	1		/* GRPQUOTA */\
+	1,		/* GRPQUOTA */\
+	1		/* PRJQUOTA */\
 }
 
 /* First generic header */
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 80d345a..f1b25f8 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -50,6 +50,7 @@
 
 #undef USRQUOTA
 #undef GRPQUOTA
+#undef PRJQUOTA
 enum quota_type {
 	USRQUOTA = 0,		/* element used for user quotas */
 	GRPQUOTA = 1,		/* element used for group quotas */
@@ -312,6 +313,7 @@ struct dquot_operations {
 	/* get reserved quota for delayed alloc, value returned is managed by
 	 * quota code only */
 	qsize_t *(*get_reserved_space) (struct inode *);
+	int (*get_projid) (struct inode *, kprojid_t *);/* Get project ID */
 };
 
 struct path;
diff --git a/include/uapi/linux/quota.h b/include/uapi/linux/quota.h
index 3b6cfbe..b2d9486 100644
--- a/include/uapi/linux/quota.h
+++ b/include/uapi/linux/quota.h
@@ -36,11 +36,12 @@
 #include <linux/errno.h>
 #include <linux/types.h>
 
-#define __DQUOT_VERSION__	"dquot_6.5.2"
+#define __DQUOT_VERSION__	"dquot_6.6.0"
 
-#define MAXQUOTAS 2
+#define MAXQUOTAS 3
 #define USRQUOTA  0		/* element used for user quotas */
 #define GRPQUOTA  1		/* element used for group quotas */
+#define PRJQUOTA  2		/* element used for project quotas */
 
 /*
  * Definitions for the default names of the quotas files.
@@ -48,6 +49,7 @@
 #define INITQFNAMES { \
 	"user",    /* USRQUOTA */ \
 	"group",   /* GRPQUOTA */ \
+	"project", /* PRJQUOTA */ \
 	"undefined", \
 };
 
-- 
1.7.1


^ permalink raw reply related

* [PATCH 0/4] quota: add project quota support
From: Li Xi @ 2014-09-24 14:04 UTC (permalink / raw)
  To: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
	hch, dmonakhov

The following patches propose an implementation of project quota
support for ext4. A project is an aggregate of unrelated inodes
which might scatter in different directories. Inodes belongs to a
project possesses a same identification i.e. 'project ID', just
like every inode has its user/group identification. The following
patches adds project quota as supplement to the former uer/group
quota types.

The semantics of ext4 project quota is consistent with XFS. Each
directory can have EXT4_INODE_PROJINHERIT flag set. When the
EXT4_INODE_PROJINHERIT flag of a parent directory is not set, a
newly created inode under that directory will have a default project
ID (i.e. 0). And its EXT4_INODE_PROJINHERIT flag is not set either.
When this flag is set on a directory, following rules will be kept:

1) The newly created inode under that directory will inherit both
the EXT4_INODE_PROJINHERIT flag and the project ID from its parent
directory.

2) Hard-linking a inode with different project ID into that directory
will fail with errno EXDEV.

3) Renaming a inode with different project ID into that directory
will fail with errno EXDEV. However, 'mv' command will detect this
failure and copy the renamed inode to a new inode in the directory.
Thus, this new inode will inherit both the project ID and
EXT4_INODE_PROJINHERIT flag.

4) If the project quota of that ID is being enforced, statfs() on
that directory will take the quotas as another upper limits along
with the capacity of the file system, i.e. the total block/inode
number will be the minimum of the quota limits and file system
capacity.

Changelog:
* v4 <- v3:
 - Do not check project feature when set/get project ID;
 - Use EXT4_MAXQUOTAS instead of MAXQUOTAS in ext4 patches;
 - Remove unnecessary change of fs/quota/dquot.c;
 - Remove CONFIG_QUOTA_PROJECT.
* v3 <- v2:
 - Add EXT4_INODE_PROJINHERIT semantics.
* v2 <- v1:
 - Add ioctl interface for setting/getting project;
 - Add EXT4_FEATURE_RO_COMPAT_PROJECT;
 - Add get_projid() method in struct dquot_operations;
 - Add error check of ext4_inode_projid_set/get().

v3: http://www.spinics.net/lists/linux-ext4/msg45184.html
v2: http://www.spinics.net/lists/linux-ext4/msg44695.html
v1: http://article.gmane.org/gmane.comp.file-systems.ext4/45153

Any comments or feedbacks are appreciated.

Regards,
                                         - Li Xi

Li Xi (4):
  Adds general codes to enforces project quota limits
  Adds project ID support for ext4
  Adds project quota support for ext4
  Adds ioctl interface support for ext4 project

 Documentation/filesystems/ext4.txt |    4 +
 fs/ext4/ext4.h                     |   31 +++++++--
 fs/ext4/ialloc.c                   |    6 ++
 fs/ext4/inode.c                    |   29 ++++++++-
 fs/ext4/ioctl.c                    |   85 ++++++++++++++++++++++++
 fs/ext4/namei.c                    |   10 +++
 fs/ext4/super.c                    |  124 ++++++++++++++++++++++++++++++++---
 fs/quota/dquot.c                   |   16 ++++-
 fs/quota/quota.c                   |    5 +-
 fs/quota/quotaio_v2.h              |    6 +-
 include/linux/quota.h              |    2 +
 include/uapi/linux/quota.h         |    6 +-
 12 files changed, 299 insertions(+), 25 deletions(-)


^ permalink raw reply

* Re: [PATCH] usb: gadget: f_rndis: fix usb_interface_descriptor for rndis
From: Heiko Schocher @ 2014-09-24 13:12 UTC (permalink / raw)
  To: Lars Melin
  Cc: linux-usb, Felipe Balbi, Greg Kroah-Hartman, linux-kernel, netdev,
	linux-api, Andrzej Pietrasiewicz, Michal Nazarewicz,
	Kyungmin Park, Dan Carpenter, Macpaul Lin
In-Reply-To: <5422B83A.8030406@gmail.com>

Hello Lars,

Am 24.09.2014 14:25, schrieb Lars Melin:
> On 2014-09-24 13:48, Heiko Schocher wrote:
>> 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
>>
> That is usb class, it is not the same thing as communication device class.
>> --- 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
> No, no, no.
> There is no CDC_SUBCLASS_RNDIS and you can not define one over an already used cdc subclass number, 0x04 is Multi-Channel Control Model

Ah, ok, so I have to define this values in a new header file, as there
is no current file for the USB_CLASS_MISC defines? Or is there a proper
place for them?

BTW: where do I find the "cdc subclass number, 0x04 is Multi-Channel
Control Model" define?

bye,
Heiko
-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany

^ permalink raw reply

* Re: [PATCH] usb: gadget: f_rndis: fix usb_interface_descriptor for rndis
From: Heiko Schocher @ 2014-09-24 13:12 UTC (permalink / raw)
  To: Michal Nazarewicz
  Cc: linux-usb, Felipe Balbi, Greg Kroah-Hartman, linux-kernel, netdev,
	linux-api, Andrzej Pietrasiewicz, Kyungmin Park, Dan Carpenter,
	Macpaul Lin
In-Reply-To: <xa1tbnq59x3z.fsf@mina86.com>

Hello Michal,

Am 24.09.2014 11:38, schrieb Michal Nazarewicz:
> On Wed, Sep 24 2014, Heiko Schocher<hs@denx.de>  wrote:
>> 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@denx.de>
>>
>> ---
>>
>> Cc: Felipe Balbi<balbi@ti.com>
>> Cc: Greg Kroah-Hartman<gregkh@suse.de>
>> Cc: linux-usb@vger.kernel.org
>> Cc: linux-kernel@vger.kernel.org
>> Cc: Oliver Neukum<oliver@neukum.name>
>> Cc: netdev@vger.kernel.org
>> Cc: linux-api@vger.kernel.org
>> Cc: Andrzej Pietrasiewicz<andrzej.p@samsung.com>
>> Cc: Michal Nazarewicz<mina86@mina86.com>
>> Cc: Kyungmin Park<kyungmin.park@samsung.com>
>> Cc: Dan Carpenter<dan.carpenter@oracle.com>
>> Cc: Macpaul Lin<macpaul@gmail.com>
>>
>> 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);
>>   }
>
> Does that mean that new kernels will stop working with old RNDIs
> gadgets because they stop recognising them as RNDIS?  I feel like this
> function should accept both, i.e.:

Hmm.. I am not a usb guru ... but I think, yes, you are right.
I add this to a v2 (if this patch has a chance to go in mainline).

Thanks!

bye,
Heiko
-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany

^ permalink raw reply

* [PATCHv8 1/6] fanotify: enable close-on-exec on events' fd when requested in fanotify_init()
From: Yann Droneaud @ 2014-09-24 13:11 UTC (permalink / raw)
  To: Eric Paris, Richard Guy Briggs, Al Viro, Andrew Morton
  Cc: linux-kernel, Yann Droneaud, linux-fsdevel, stable, linux-api
In-Reply-To: <cover.1411562410.git.ydroneaud@opteya.com>

According to commit 80af258867648 ('fanotify: groups can specify
their f_flags for new fd'), file descriptors created as part of
file access notification events inherit flags from the
event_f_flags argument passed to syscall fanotify_init(2).

So while it is legal for userspace to call fanotify_init() with
O_CLOEXEC as part of its second argument, O_CLOEXEC is currently
silently ignored.

Indeed event_f_flags are only given to dentry_open(), which only
seems to care about O_ACCMODE and O_PATH in do_dentry_open(),
O_DIRECT in open_check_o_direct() and O_LARGEFILE in
generic_file_open().

More, there's no effective check on event_f_flags value that
would catch unknown / unsupported values, unlike the one on
f_flags argument of the syscall (see FAN_ALL_INIT_FLAGS in
include/uapi/linux/fanotify.h).

Reading article "Botching up ioctls"[1] by Daniel Vetter might
make one feel uncomfortable when trying to add extension to an
API that doesn't check for unrecognized values.

But it seems logical to set close-on-exec flag on the file
descriptor if userspace is allowed to request it with O_CLOEXEC.

In fact, according to some lookup on http://codesearch.debian.net/
and various search engine, there's already some userspace code
requesting it:

- in systemd's readahead[2]:

    fanotify_fd = fanotify_init(FAN_CLOEXEC|FAN_NONBLOCK, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME);

- in clsync[3]:

    #define FANOTIFY_EVFLAGS (O_LARGEFILE|O_RDONLY|O_CLOEXEC)

    int fanotify_d = fanotify_init(FANOTIFY_FLAGS, FANOTIFY_EVFLAGS);

- in examples [4] from "Filesystem monitoring in the Linux
  kernel" article[5] by Aleksander Morgado:

    if ((fanotify_fd = fanotify_init (FAN_CLOEXEC,
                                      O_RDONLY | O_CLOEXEC | O_LARGEFILE)) < 0)

Lookup also returned some wrong usage of the syscall:

- in Gonk HAL from Mozilla Firefox OS sources[6]:

    mFd = fanotify_init(FAN_CLASS_NOTIF, FAN_CLOEXEC);

Adding support for O_CLOEXEC in fanotify_init() won't magically
enable it for Gonk since FAN_CLOEXEC is defined as 0x1, which
is likely equal to O_WRONLY when used in open flag context. In
the other hand, it won't hurt it either.

So this patch replaces call to macro get_unused_fd() by a call
to function get_unused_fd_flags() with event_f_flags value as
argument. This way O_CLOEXEC flag in the second argument of
fanotify_init(2) syscall is interpreted so that close-on-exec
get enabled when requested.

[1] http://blog.ffwll.ch/2013/11/botching-up-ioctls.html
[2] http://cgit.freedesktop.org/systemd/systemd/tree/src/readahead/readahead-collect.c?id=v208#n294
[3] https://github.com/xaionaro/clsync/blob/v0.2.1/sync.c#L1631
    https://github.com/xaionaro/clsync/blob/v0.2.1/configuration.h#L38
[4] http://www.lanedo.com/~aleksander/fanotify/fanotify-example.c
[5] http://www.lanedo.com/2013/filesystem-monitoring-linux-kernel/
[6] http://hg.mozilla.org/mozilla-central/file/325c74addeba/hal/gonk/GonkDiskSpaceWatcher.cpp#l167

Link: http://lkml.kernel.org/r/cover.1411562410.git.ydroneaud@opteya.com
Cc: Richard Guy Briggs <rgb@redhat.com>
Cc: Eric Paris <eparis@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: stable@vger.kernel.org
Cc: linux-api@vger.kernel.org
Signed-off-by: Yann Droneaud <ydroneaud@opteya.com>
---
 fs/notify/fanotify/fanotify_user.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index b13992a41bd9..c991616acca9 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -78,7 +78,7 @@ static int create_fd(struct fsnotify_group *group,
 
 	pr_debug("%s: group=%p event=%p\n", __func__, group, event);
 
-	client_fd = get_unused_fd();
+	client_fd = get_unused_fd_flags(group->fanotify_data.f_flags);
 	if (client_fd < 0)
 		return client_fd;
 
-- 
1.9.3

^ permalink raw reply related

* [PATCHv8 0/6] Getting rid of get_unused_fd() / enable close-on-exec
From: Yann Droneaud @ 2014-09-24 13:11 UTC (permalink / raw)
  To: Richard Guy Briggs, Eric Paris, Tony Luck, Fenghua Yu,
	linux-ia64-u79uwXL29TY76Z2rM5mHXA, Jeremy Kerr, Arnd Bergmann,
	Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ,
	cbe-oss-dev-uLR06cmDAlY/bJ5BZ2RsiQ, Al Viro,
	linux-fsdevel-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, Jiri Kosina
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Yann Droneaud,
	linux-api-u79uwXL29TY76Z2rM5mHXA, Ulrich Drepper

TL;DR:

- Trivial patches to replace calls to get_unused_fd() by
  get_unused_fd_flags(0);
- Patch to remove get_unused_fd();
- Patch to add support O_CLOEXEC in fanotify_init().

Hi,

Please find the eighth revision of my patchset to remove
get_unused_fd() macro in order to help subsystems to use
get_unused_fd_flags() (or anon_inode_getfd()) with flags either
provided by the userspace or set to O_CLOEXEC by default where
appropriate.

Without get_unused_fd() macro, more subsystems are likely to use
get_unused_fd_flags() (or anon_inode_getfd()) and be taught to
provide an API that let userspace choose the opening flags of
the file descriptor.

Not allowing userspace to provide the "open" flags or not using
O_CLOEXEC by default should be considered bad practice from
security point of view: in most case O_CLOEXEC must be used to
not leak file descriptor across exec().

Not allowing userspace to atomically set close-on-exec flag and
not using O_CLOEXEC should be avoided to protect multi-threaded
program from race condition when it tried to set close-on-exec
flag using fcntl(fd, F_SETFD, FD_CLOEXEC) after opening the file
descriptor.

Example:

    int fd;
    int ret;

    fd = open(filename, O_RDONLY);
    if (fd < 0) {
        perror("open");
        return -1;
    }

    /*
     * window opened for another thread to call fork(),
     * then the new process can call exec() at any time
     * and the file descriptor would be inherited
     */

    ret = fcntl(fd, F_SETFD, FD_CLOEXEC)
    if (ret < 0) {
        perror("fnctl()");
        close(fd);
        return -1;
    }

vs.:

    int fd;
    fd = open(filename, O_RDONLY | O_CLOEXEC);
    if (fd < 0) {
        perror("open");
        return -1;
    }

Using O_CLOEXEC by default when flags are not (eg. cannot be)
provided by userspace is the safest bet as it allows userspace
to choose, if, when and where the file descriptor is going to be
inherited across exec(): userspace is free to call fcntl() to
remove FD_CLOEXEC flag in the child process that will call
exec().

Unfortunately, O_CLOEXEC cannot be made the default for most
existing system calls as it's not the default behavior for
POSIX / Unix. Reader interested in this issue could have a look
at "Ghosts of Unix past, part 2: Conflated designs" [1] article
by Neil Brown.

FAQ:

- Why do one want close-on-exec ?

Setting close-on-exec flag on file descriptor ensure it won't be
inherited silently by child, child of child, etc. when executing
another program.

If the file descriptor is not closed, some kernel resources can
be locked until the last process with the opened file descriptor
exit.

If the file descriptor is not closed, this can lead to a
security issue, eg. making resources available to a less
privileged program allowing information leak and/or deny of
service.

- Why do one need atomic close-on-exec ?

Even if it's possible to set close-on-exec flag through call to
fcntl() as shown previously, it introduces a race condition in
multi-threaded process, where a thread call fork() / exec()
while another thread is between call to open() and fcntl().

Additionally, using close-on-exec free the programmer from
tracking manually which file descriptor is to be closed in a
child before calling exec(): in a program using multiple
third-party libraries, it's difficult to know which file
descriptor must be closed. AFAIK, while there's a atexit(),
pthread_atfork(), there's no atexec() userspace function
in libc to allow libraries to register a handler in order
to close its opened file descriptor before exec().

- Why default to close-on-exec ?

Some kernel interfaces don't allow userspace to pass a
O_CLOEXEC-like flag when creating a new file descriptor.

In such cases, if possible (see below), O_CLOEXEC must be made
the default so that userspace doesn't have to call fcntl()
which, as demonstrated previously, is open to race condition
in multi-threaded program.

- How to choose between flag 0 or O_CLOEXEC in call to
  get_unused_fd_flags() (or anon_inode_getfd()) ?

Short: Will it break existing application ? Will it break kernel
       ABI ?

       If answer is no, use O_CLOEXEC.
       If answer is yes, use 0.

Long: If userspace expect to retrieve a file descriptor with
      plain old Unix(tm) semantics, O_CLOEXEC must not be made
      the default, as it could break some applications expecting
      that the file descriptor will be inherited across exec().

      But for some subsystems, such as InfiniBand, KVM, VFIO,
      etc. it makes no sense to have file descriptors inherited
      across exec() since those are tied to resources that will
      vanish when a another program will replace the current
      one by mean of exec(), so it's safe to use O_CLOEXEC in
      such cases.

      For others, like XFS, the file descriptor is retrieved by
      one program and will be used by a different program,
      executed as a child. In this case, setting O_CLOEXEC would
      break existing application which do not expect to have to
      call fcntl(fd, F_SETFD, 0) to make it available across
      exec().

      If file descriptor created by a subsystem is not tied to
      the current process resources, it's likely legal to use it
      in a different process context, thus O_CLOEXEC must not be
      the default.

      If one, as a subsystem maintainer, cannot tell for sure
      that no userspace program ever rely current behavior, eg.
      file descriptor being inherited across exec(), then the
      default flag *must* be kept 0 to not break application.

- This subsystem cannot be turned to use O_CLOEXEC by default:

If O_CLOEXEC cannot be made the default, it would be interesting
to think to extend the API to have a (set of) function(s) taking
a flag parameter so that userspace can atomically request
close-on-exec if it need it (and it should need it !).

- Background:

One might want to read "Secure File Descriptor Handling" [2] by
Ulrich Drepper who is responsible of adding O_CLOEXEC flag on
open(), and flags alike on other syscalls.

One might also want to read PEP-446 "Make newly created file
descriptors non-inheritable" [3] by Victor Stinner since it has
lot more background information on file descriptor leaking.

One would also like to read "Excuse me son, but your code is
leaking !!!" [4] by Dan Walsh for advice.

[1] http://lwn.net/Articles/412131/
[2] http://udrepper.livejournal.com/20407.html
[3] http://www.python.org/dev/peps/pep-0446/
[4] http://danwalsh.livejournal.com/53603.html

- Statistics:

In linux-next, tag next-20140924, they're currently:

- 33 calls to fd_install()
       with one call part of anon_inode_getfd()
- 26 calls to get_unused_fd_flags()
       with one call part of anon_inode_getfd()
       with another part of get_unused_fd() macro
- 11 calls to anon_inode_getfd()
-  8 calls to anon_inode_getfile()
       with one call part of anon_inode_getfd()
-  6 calls to get_unused_fd()

Changes from patchset v7 [PATCHSETv7]

- Rebased on top of latest -next
- Simplified commit message for trivial patches
- Proofread commit messages
- Addded CC: linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org

Changes from patchset v6 [PATCHSETv6]

- Rebased on top of latest -next
- Added Cc: trivial-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org for the first trivials
  patches.

Changes from patchset v5 [PATCHSETv5]

- perf: introduce a flag to enable close-on-exec in
  perf_event_open()
  DROPPED: applied upstream, commit a21b0b354d4a.

Changes from patchset v4 [PATCHSETv4]:

- rewrote cover letter following discussion with perf
  maintainer. Thanks to Peter Zijlstra.

- modified a bit some commit messages.

- events: use get_unused_fd_flags(0) instead of get_unused_fd()
  DROPPED: replaced by following patch.

- perf: introduce a flag to enable close-on-exec in
  perf_event_open()
  NEW: instead of hard coding the flags to 0, this patch
       allows userspace to specify close-on-exec flag.

- fanotify: use get_unused_fd_flags(0) instead of get_unused_fd()
  DROPPED: replaced by following patch.

- fanotify: enable close-on-exec on events' fd when requested in
    fanotify_init()
  NEW: instead of hard coding the flags to 0, this patch
       enable close-on-exec if userspace request it.

Changes from patchset v3 [PATCHSETv3]:

- industrialio: use anon_inode_getfd() with O_CLOEXEC flag
  DROPPED: applied upstream, commit a646fbf0fd11.

Changes from patchset v2 [PATCHSETv2]:

- android/sw_sync: use get_unused_fd_flags(O_CLOEXEC) instead
  of get_unused_fd()
  DROPPED: applied upstream, commit 45acea57335e.

- android/sync: use get_unused_fd_flags(O_CLOEXEC) instead of
  get_unused_fd()
  DROPPED: applied upstream, commit 9c6cd3b39048.

- vfio: use get_unused_fd_flags(0) instead of get_unused_fd()
  DROPPED: applied upstream, commit a5d550703d2c.
  Additionally subsystem maintainer applied another patch on top
  to set the flags to O_CLOEXEC, commit 5d042fbdbb2d.

- industrialio: use anon_inode_getfd() with O_CLOEXEC flag
  NEW: propose to use O_CLOEXEC as default flag.

Changes from patchset v1 [PATCHSETv1]:

- explicitly added subsystem maintainers as mail recepients.

- infiniband: use get_unused_fd_flags(0) instead of
  get_unused_fd()
  DROPPED: subsystem maintainer applied another patch
           using get_unused_fd_flags(O_CLOEXEC) as suggested,
	   commit da183c7af844.

- android/sw_sync: use get_unused_fd_flags(0) instead of
  get_unused_fd()
  MODIFIED: use get_unused_fd_flags(O_CLOEXEC) as suggested.

- android/sync: use get_unused_fd_flags(0) instead of
  get_unused_fd()
  MODIFIED: use get_unused_fd_flags(O_CLOEXEC) as suggested.

- xfs: use get_unused_fd_flags(0) instead of get_unused_fd()
  DROPPED: applied asis by subsystem maintainer, commit 862a62937e76.

- sctp: use get_unused_fd_flags(0) instead of get_unused_fd()
  DROPPED: applied asis by subsystem maintainer, commit 8a59bd3e9b29.

Links:

[PATCHSETv7]
  http://lkml.kernel.org/r/cover.1401630396.git.ydroneaud-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.org

[PATCHSETv6]
  http://lkml.kernel.org/r/cover.1394532336.git.ydroneaud-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.org

[PATCHSETv5]
  http://lkml.kernel.org/r/cover.1388952061.git.ydroneaud-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.org

[PATCHSETv4]
  http://lkml.kernel.org/r/cover.1383121137.git.ydroneaud-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.org

[PATCHSETv3]
  http://lkml.kernel.org/r/cover.1378460926.git.ydroneaud-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.org

[PATCHSETv2]
  http://lkml.kernel.org/r/cover.1376327678.git.ydroneaud-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.org

[PATCHSETv1]
  http://lkml.kernel.org/r/cover.1372777600.git.ydroneaud-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.org

Yann Droneaud (6):
  fanotify: enable close-on-exec on events' fd when requested in
    fanotify_init()
  ia64: trivial: replace get_unused_fd() by get_unused_fd_flags(0)
  ppc/cell: trivial: replace get_unused_fd() by get_unused_fd_flags(0)
  binfmt_misc: trivial: replace get_unused_fd() by
    get_unused_fd_flags(0)
  file: trivial: replace get_unused_fd() by get_unused_fd_flags(0)
  file: remove get_unused_fd() macro

 arch/ia64/kernel/perfmon.c                | 2 +-
 arch/powerpc/platforms/cell/spufs/inode.c | 4 ++--
 fs/binfmt_misc.c                          | 2 +-
 fs/file.c                                 | 2 +-
 fs/notify/fanotify/fanotify_user.c        | 2 +-
 include/linux/file.h                      | 1 -
 6 files changed, 6 insertions(+), 7 deletions(-)

-- 
1.9.3

^ permalink raw reply

* Re: [PATCH v4 0/4] quota: add project quota support
From: Jan Kara @ 2014-09-24 12:52 UTC (permalink / raw)
  To: Li Xi
  Cc: linux-fsdevel@vger.kernel.org, Ext4 Developers List, linux-api,
	Theodore Ts'o, Andreas Dilger, Jan Kara,
	viro@zeniv.linux.org.uk, hch@infradead.org, Dmitry Monakhov
In-Reply-To: <CAPTn0cDFCj7AWjQpOF0brD82XJSSeem5_1aQCE=zQKiaLaeL1g@mail.gmail.com>

On Wed 24-09-14 20:14:35, Li Xi wrote:
> The following patches propose an implementation of project quota
> support for ext4. A project is an aggregate of unrelated inodes
> which might scatter in different directories. Inodes belongs to a
> project possesses a same identification i.e. 'project ID', just
> like every inode has its user/group identification. The following
> patches adds project quota as supplement to the former uer/group
> quota types.
> 
> The semantics of ext4 project quota is consistent with XFS. Each
> directory can have EXT4_INODE_PROJINHERIT flag set. When the
> EXT4_INODE_PROJINHERIT flag of a parent directory is not set, a
> newly created inode under that directory will have a default project
> ID (i.e. 0). And its EXT4_INODE_PROJINHERIT flag is not set either.
> When this flag is set on a directory, following rules will be kept:
> 
> 1) The newly created inode under that directory will inherit both
> the EXT4_INODE_PROJINHERIT flag and the project ID from its parent
> directory.
> 
> 2) Hard-linking a inode with different project ID into that directory
> will fail with errno EXDEV.
> 
> 3) Renaming a inode with different project ID into that directory
> will fail with errno EXDEV. However, 'mv' command will detect this
> failure and copy the renamed inode to a new inode in the directory.
> Thus, this new inode will inherit both the project ID and
> EXT4_INODE_PROJINHERIT flag.
> 
> 4) If the project quota of that ID is being enforced, statfs() on
> that directory will take the quotas as another upper limits along
> with the capacity of the file system, i.e. the total block/inode
> number will be the minimum of the quota limits and file system
> capacity.
> 
> Changelog:
> * v4 <- v3:
>  - Do not check project feature when set/get project ID;
>  - Use EXT4_MAXQUOTAS instead of MAXQUOTAS in ext4 patches;
>  - Remove unnecessary change of fs/quota/dquot.c;
>  - Remove CONFIG_QUOTA_PROJECT.
> * v3 <- v2:
>  - Add EXT4_INODE_PROJINHERIT semantics.
> * v2 <- v1:
>  - Add ioctl interface for setting/getting project;
>  - Add EXT4_FEATURE_RO_COMPAT_PROJECT;
>  - Add get_projid() method in struct dquot_operations;
>  - Add error check of ext4_inode_projid_set/get().
> 
> v3: http://www.spinics.net/lists/linux-ext4/msg45184.html
> v2: http://www.spinics.net/lists/linux-ext4/msg44695.html
> v1: http://article.gmane.org/gmane.comp.file-systems.ext4/45153
> 
> Any comments or feedbacks are appreciated.
  Whitespace got mangled in your patches so much they are hardly readable.
Please resend them so that it is properly preserved. I'm not sure how other
guys using gmail do this so you might need to ask them... Thanks!

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

^ permalink raw reply

* [PATCH] [media] Fix smatch warning: unknown field name in initializer
From: Mauro Carvalho Chehab @ 2014-09-24 12:51 UTC (permalink / raw)
  To: Linux Media Mailing List
  Cc: Mauro Carvalho Chehab, Mauro Carvalho Chehab,
	linux-api-u79uwXL29TY76Z2rM5mHXA

This is detected with:
	gcc-4.8.3-7.fc20.x86_64

Smatch, up to this patch:
	commit de462ba2c79d9347368c887ed93113e7818a7b07
	Author: Dan Carpenter <dan.carpenter-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org>
	Date:   Wed Sep 17 13:31:16 2014 +0300

drivers/media/v4l2-core/v4l2-dv-timings.c:34:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:35:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:36:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:37:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:38:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:39:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:40:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:41:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:42:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:43:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:44:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:45:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:46:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:47:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:48:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:49:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:50:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:51:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:52:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:53:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:54:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:55:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:56:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:57:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:58:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:59:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:60:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:61:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:62:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:63:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:64:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:65:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:66:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:67:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:68:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:69:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:70:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:71:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:72:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:73:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:74:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:75:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:76:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:77:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:78:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:79:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:80:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:81:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:82:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:83:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:84:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:85:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:86:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:87:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:88:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:89:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:90:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:91:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:92:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:93:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:94:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:95:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:96:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:97:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:98:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:99:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:100:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:101:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:102:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:103:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:104:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:105:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:106:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:107:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:108:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:109:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:110:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:111:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:112:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:113:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:114:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:115:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:116:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:117:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:118:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:119:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:120:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:121:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:122:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:123:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:124:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:125:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:126:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:127:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:128:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:129:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:130:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:131:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:132:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:133:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:134:9: error: unknown field name in initializer
drivers/media/v4l2-core/v4l2-dv-timings.c:135:9: error: too many errors
drivers/media/usb/hdpvr/hdpvr-video.c:42:9: error: unknown field name in initializer
drivers/media/usb/hdpvr/hdpvr-video.c:43:9: error: unknown field name in initializer
drivers/media/usb/hdpvr/hdpvr-video.c:44:9: error: unknown field name in initializer
drivers/media/usb/hdpvr/hdpvr-video.c:45:9: error: unknown field name in initializer
drivers/media/usb/hdpvr/hdpvr-video.c:46:9: error: unknown field name in initializer
drivers/media/usb/hdpvr/hdpvr-video.c:47:9: error: unknown field name in initializer
drivers/media/usb/hdpvr/hdpvr-video.c:48:9: error: unknown field name in initializer
drivers/media/usb/hdpvr/hdpvr-video.c:49:9: error: unknown field name in initializer
drivers/media/platform/s5p-tv/hdmi_drv.c:484:18: error: unknown field name in initializer
drivers/media/platform/s5p-tv/hdmi_drv.c:485:18: error: unknown field name in initializer
drivers/media/platform/s5p-tv/hdmi_drv.c:486:18: error: unknown field name in initializer
drivers/media/platform/s5p-tv/hdmi_drv.c:487:18: error: unknown field name in initializer
drivers/media/platform/s5p-tv/hdmi_drv.c:488:18: error: unknown field name in initializer
drivers/media/platform/s5p-tv/hdmi_drv.c:489:18: error: unknown field name in initializer
drivers/media/platform/s5p-tv/hdmi_drv.c:490:18: error: unknown field name in initializer
drivers/media/platform/s5p-tv/hdmi_drv.c:491:18: error: unknown field name in initializer
drivers/media/platform/s5p-tv/hdmi_drv.c:492:18: error: unknown field name in initializer
drivers/media/platform/s5p-tv/hdmi_drv.c:493:18: error: unknown field name in initializer

Signed-off-by: Mauro Carvalho Chehab <mchehab-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>

diff --git a/include/uapi/linux/v4l2-dv-timings.h b/include/uapi/linux/v4l2-dv-timings.h
index 6c8f159e416e..6a0764c89fcb 100644
--- a/include/uapi/linux/v4l2-dv-timings.h
+++ b/include/uapi/linux/v4l2-dv-timings.h
@@ -21,17 +21,8 @@
 #ifndef _V4L2_DV_TIMINGS_H
 #define _V4L2_DV_TIMINGS_H
 
-#if __GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 6))
-/* Sadly gcc versions older than 4.6 have a bug in how they initialize
-   anonymous unions where they require additional curly brackets.
-   This violates the C1x standard. This workaround adds the curly brackets
-   if needed. */
 #define V4L2_INIT_BT_TIMINGS(_width, args...) \
 	{ .bt = { _width , ## args } }
-#else
-#define V4L2_INIT_BT_TIMINGS(_width, args...) \
-	.bt = { _width , ## args }
-#endif
 
 /* CEA-861-E timings (i.e. standard HDTV timings) */
 
-- 
1.9.3

^ permalink raw reply related

* Re: [PATCH] usb: gadget: f_rndis: fix usb_interface_descriptor for rndis
From: Lars Melin @ 2014-09-24 12:25 UTC (permalink / raw)
  To: Heiko Schocher, linux-usb-u79uwXL29TY76Z2rM5mHXA
  Cc: 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
In-Reply-To: <1411541339-32400-1-git-send-email-hs-ynQEQJNshbs@public.gmane.org>

On 2014-09-24 13:48, Heiko Schocher wrote:
> 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
>
That is usb class, it is not the same thing as communication device class.
> --- 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
No, no, no.
There is no CDC_SUBCLASS_RNDIS and you can not define one over an 
already used cdc subclass number,  0x04 is  Multi-Channel Control Model

^ permalink raw reply

* [PATCH v4 0/4] quota: add project quota support
From: Li Xi @ 2014-09-24 12:23 UTC (permalink / raw)
  To: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Ext4 Developers List, linux-api-u79uwXL29TY76Z2rM5mHXA,
	Theodore Ts'o, Andreas Dilger, Jan Kara,
	viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org,
	hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org, Dmitry Monakhov

The following patches propose an implementation of project quota
support for ext4. A project is an aggregate of unrelated inodes
which might scatter in different directories. Inodes belongs to a
project possesses a same identification i.e. 'project ID', just
like every inode has its user/group identification. The following
patches adds project quota as supplement to the former uer/group
quota types.

The semantics of ext4 project quota is consistent with XFS. Each
directory can have EXT4_INODE_PROJINHERIT flag set. When the
EXT4_INODE_PROJINHERIT flag of a parent directory is not set, a
newly created inode under that directory will have a default project
ID (i.e. 0). And its EXT4_INODE_PROJINHERIT flag is not set either.
When this flag is set on a directory, following rules will be kept:

1) The newly created inode under that directory will inherit both
the EXT4_INODE_PROJINHERIT flag and the project ID from its parent
directory.

2) Hard-linking a inode with different project ID into that directory
will fail with errno EXDEV.

3) Renaming a inode with different project ID into that directory
will fail with errno EXDEV. However, 'mv' command will detect this
failure and copy the renamed inode to a new inode in the directory.
Thus, this new inode will inherit both the project ID and
EXT4_INODE_PROJINHERIT flag.

4) If the project quota of that ID is being enforced, statfs() on
that directory will take the quotas as another upper limits along
with the capacity of the file system, i.e. the total block/inode
number will be the minimum of the quota limits and file system
capacity.

Changelog:
* v4 <- v3:
 - Do not check project feature when set/get project ID;
 - Use EXT4_MAXQUOTAS instead of MAXQUOTAS in ext4 patches;
 - Remove unnecessary change of fs/quota/dquot.c;
 - Remove CONFIG_QUOTA_PROJECT.
* v3 <- v2:
 - Add EXT4_INODE_PROJINHERIT semantics.
* v2 <- v1:
 - Add ioctl interface for setting/getting project;
 - Add EXT4_FEATURE_RO_COMPAT_PROJECT;
 - Add get_projid() method in struct dquot_operations;
 - Add error check of ext4_inode_projid_set/get().

v3: http://www.spinics.net/lists/linux-ext4/msg45184.html
v2: http://www.spinics.net/lists/linux-ext4/msg44695.html
v1: http://article.gmane.org/gmane.comp.file-systems.ext4/45153

Any comments or feedbacks are appreciated.

Regards,
                                         - Li Xi

Li Xi(4):
quota: Adds general codes to enforces project quota limits
ext4: Adds project ID support for ext4
ext4: Adds project quota support for ext4
ext4: Adds ioctl interface support for ext4 project

Documentation/filesystems/ext4.txt |    4 +
fs/ext4/ext4.h                     |   31 +++++++--
fs/ext4/ialloc.c                   |    6 ++
fs/ext4/inode.c                    |   29 ++++++++-
fs/ext4/ioctl.c                    |   85 ++++++++++++++++++++++++
fs/ext4/namei.c                    |   10 +++
fs/ext4/super.c                    |  124 ++++++++++++++++++++++++++++++++---
fs/quota/dquot.c                   |   16 ++++-
fs/quota/quota.c                   |    5 +-
fs/quota/quotaio_v2.h              |    6 +-
include/linux/quota.h              |    2 +
include/uapi/linux/quota.h         |    6 +-
12 files changed, 299 insertions(+), 25 deletions(-)

^ permalink raw reply

* Re: [PATCH v7 0/8] charger/mfd: max14577: Add support for MAX77836
From: Lee Jones @ 2014-09-24 11:55 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Samuel Ortiz, linux-kernel, linux-api, linux-pm,
	Sebastian Reichel, Dmitry Eremin-Solenikov, David Woodhouse,
	Liam Girdwood, Mark Brown, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Kyungmin Park, Marek Szyprowski,
	Bartlomiej Zolnierkiewicz, devicetree, Andrew Morton
In-Reply-To: <20140924100325.GB19999@lee--X1>

Mark, Sebastian,

Please don't pull this in yet!  There is an issue with the set.

I'll reply to this mail once it's been rectified.

Kind regards,
Lee

> The following changes since commit a497c3ba1d97fc69c1e78e7b96435ba8c2cb42ee:
> 
>   Linux 3.16-rc2 (2014-06-21 19:02:54 -1000)
> 
> are available in the git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git tags/mfd-regulator-power-v3.17
> 
> for you to fetch changes up to f453d80f908254293bf453a2d7e68974e2abc2af:
> 
>   Documentation: charger: max14577: Document exported sysfs entry (2014-09-24 10:58:07 +0100)
> 
> ----------------------------------------------------------------
> Immutable branch between MFD, Regulator and Power due for v3.17
> 
> ----------------------------------------------------------------
> Krzysztof Kozlowski (8):
>       mfd: max14577: Add defines for MAX77836 charger
>       mfd: max14577: Map charger device to its own of_node
>       charger: max14577: Add support for MAX77836 charger
>       regulator/mfd: max14577: Export symbols for calculating charger current
>       charger: max14577: Configure battery-dependent settings from DTS and sysfs
>       power: max17040: Add ID for MAX77836 Fuel Gauge block
>       devicetree: mfd: max14577: Add device tree bindings document
>       Documentation: charger: max14577: Document exported sysfs entry
> 
>  Documentation/ABI/testing/sysfs-class-power        |  14 +
>  Documentation/devicetree/bindings/mfd/max14577.txt | 146 ++++++++
>  drivers/mfd/max14577.c                             | 100 +++++-
>  drivers/power/Kconfig                              |   5 +-
>  drivers/power/max14577_charger.c                   | 370 +++++++++++++++++++--
>  drivers/power/max17040_battery.c                   |   3 +-
>  drivers/regulator/max14577.c                       |  80 +----
>  include/linux/mfd/max14577-private.h               |  95 ++++--
>  include/linux/mfd/max14577.h                       |  30 ++
>  9 files changed, 704 insertions(+), 139 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/mfd/max14577.txt
> 

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply

* Re: [PATCH v4 1/5] drm/rockchip: Add basic drm driver
From: Daniel Vetter @ 2014-09-24 11:20 UTC (permalink / raw)
  To: Mark yao
  Cc: Heiko Stübner, 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, linux-doc, Kever Yang, dri-devel,
	Douglas Anderson, Jianqun Xu, Chris Zhong, jeff chen,
	linux-rockchip, kfx
In-Reply-To: <54228F7B.2050608@rock-chips.com>

On Wed, Sep 24, 2014 at 11:31 AM, Mark yao <mark.yao@rock-chips.com> wrote:
> On 2014年09月24日 16:20, Daniel Vetter wrote:
>>
>> On Mon, Sep 22, 2014 at 06:48:54PM +0800, Mark yao wrote:
>>>
>>> This patch adds the basic structure of a DRM Driver for Rockchip Socs.
>>>
>>> Signed-off-by: Mark yao <mark.yao@rock-chips.com>
>>> ---
>>> Changes in v2:
>>> - use the component framework to defer main drm driver probe
>>>    until all VOP devices have been probed.
>>> - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
>>>    master device and each vop device can shared the drm dma mapping.
>>> - use drm_crtc_init_with_planes and drm_universal_plane_init.
>>> - remove unnecessary middle layers.
>>> - add cursor set, move funcs to rockchip drm crtc.
>>> - use vop reset at first init
>>> - reference framebuffer when used and unreference when swap out vop
>>>
>>> Changes in v3:
>>> - change "crtc->fb" to "crtc->primary-fb"
>>> Adviced by Daniel Vetter
>>> - init cursor plane with universal api, remove unnecessary cursor
>>> set,move
>>>
>>> Changes in v4:
>>> Adviced by David Herrmann
>>> - remove drm_platform_*() usage, use register drm device directly.
>>> Adviced by Rob Clark
>>> - remove special mmap ioctl, do userspace mmap with normal mmap() or mmap
>>> offset
>>>
>>>   drivers/gpu/drm/Kconfig                       |    2 +
>>>   drivers/gpu/drm/Makefile                      |    1 +
>>>   drivers/gpu/drm/rockchip/Kconfig              |   19 +
>>>   drivers/gpu/drm/rockchip/Makefile             |   10 +
>>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |  524 ++++++++++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |  120 +++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c    |  201 ++++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.h    |   28 +
>>>   drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c |  231 +++++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h |   20 +
>>>   drivers/gpu/drm/rockchip/rockchip_drm_gem.c   |  404 ++++++++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_gem.h   |   72 ++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c   | 1372
>>> +++++++++++++++++++++++++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |  187 ++++
>>>   include/uapi/drm/rockchip_drm.h               |   75 ++
>>
>> uapi is still here ... Was this an oversight?
>> -Daniel
>>
> Hi, Daniel
> this version is old, newest is v5. and I remove uapi at v5.
> you can see v5 patch at:
> https://lkml.org/lkml/2014/9/23/1061
> thanks

This version doesn't seem to be cc'ed to dri-devel, at least it didn't
yet show up. Can you please double-check?

Thanks, Daniel

^ permalink raw reply

* Re: [PATCH v4 5/5] drm/rockchip: Add support for Rockchip Soc EDP
From: jeff chen @ 2014-09-24 10:30 UTC (permalink / raw)
  To: Rob Clark
  Cc: Mark Rutland, Heiko Stübner, linux-doc, Kever Yang,
	dri-devel@lists.freedesktop.org, Linux Kernel Mailing List,
	Jianqun Xu, Chris Zhong, linux-api, David Airlie, Boris BREZILLON,
	simon xue, linux-rockchip, kfx, Grant Likely,
	王晓腾, Tao Huang, devicetree@vger.kernel.org,
	Pawel Moll, Ian Campbell, dbehr, yxj, Eddie Cai
In-Reply-To: <CAF6AEGtZGypyV4EnWZr4LziEnOriKZr1ggn+AtkuPnf2ud5gBw@mail.gmail.com>


On 2014/9/24 7:35, Rob Clark wrote:
> On Tue, Sep 23, 2014 at 9:56 AM, Rob Clark <robdclark@gmail.com> wrote:
>> On Tue, Sep 23, 2014 at 4:47 AM, cym <cym@rock-chips.com> 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@rock-chips.com> wrote:
>>>>> This adds support for Rockchip soc edp found on rk3288
>>>>>
>>>>> Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
>>>>> Signed-off-by: Jeff Chen <jeff.chen@rock-chips.com>
>>>>> ---
>>>>> 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@rock-chips.com>
>>>>> +*      Jeff chen <jeff.chen@rock-chips.com>
>>>>> +*
>>>>> +* 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
thanks,I will modify the core part of the driver.

BR,
-Jeff
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>
>

^ permalink raw reply

* Re: [PATCH v7 0/8] charger/mfd: max14577: Add support for MAX77836
From: Lee Jones @ 2014-09-24 10:03 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Samuel Ortiz, linux-kernel, linux-api, linux-pm,
	Sebastian Reichel, Dmitry Eremin-Solenikov, David Woodhouse,
	Liam Girdwood, Mark Brown, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Kyungmin Park, Marek Szyprowski,
	Bartlomiej Zolnierkiewicz, devicetree, Andrew Morton
In-Reply-To: <1411472603-11821-1-git-send-email-k.kozlowski@samsung.com>

Mark, Sebastian,

The following changes since commit a497c3ba1d97fc69c1e78e7b96435ba8c2cb42ee:

  Linux 3.16-rc2 (2014-06-21 19:02:54 -1000)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git tags/mfd-regulator-power-v3.17

for you to fetch changes up to f453d80f908254293bf453a2d7e68974e2abc2af:

  Documentation: charger: max14577: Document exported sysfs entry (2014-09-24 10:58:07 +0100)

----------------------------------------------------------------
Immutable branch between MFD, Regulator and Power due for v3.17

----------------------------------------------------------------
Krzysztof Kozlowski (8):
      mfd: max14577: Add defines for MAX77836 charger
      mfd: max14577: Map charger device to its own of_node
      charger: max14577: Add support for MAX77836 charger
      regulator/mfd: max14577: Export symbols for calculating charger current
      charger: max14577: Configure battery-dependent settings from DTS and sysfs
      power: max17040: Add ID for MAX77836 Fuel Gauge block
      devicetree: mfd: max14577: Add device tree bindings document
      Documentation: charger: max14577: Document exported sysfs entry

 Documentation/ABI/testing/sysfs-class-power        |  14 +
 Documentation/devicetree/bindings/mfd/max14577.txt | 146 ++++++++
 drivers/mfd/max14577.c                             | 100 +++++-
 drivers/power/Kconfig                              |   5 +-
 drivers/power/max14577_charger.c                   | 370 +++++++++++++++++++--
 drivers/power/max17040_battery.c                   |   3 +-
 drivers/regulator/max14577.c                       |  80 +----
 include/linux/mfd/max14577-private.h               |  95 ++++--
 include/linux/mfd/max14577.h                       |  30 ++
 9 files changed, 704 insertions(+), 139 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/max14577.txt

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply

* Re: [PATCH] usb: gadget: f_rndis: fix usb_interface_descriptor for rndis
From: Michal Nazarewicz @ 2014-09-24  9:38 UTC (permalink / raw)
  To: linux-usb
  Cc: Heiko Schocher, Felipe Balbi, Greg Kroah-Hartman, linux-kernel,
	Oliver Neukum, netdev, linux-api, Andrzej Pietrasiewicz,
	Kyungmin Park, Dan Carpenter, Macpaul Lin
In-Reply-To: <1411541339-32400-1-git-send-email-hs@denx.de>

On Wed, Sep 24 2014, Heiko Schocher <hs@denx.de> wrote:
> 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@denx.de>
>
> ---
>
> Cc: Felipe Balbi <balbi@ti.com>
> Cc: Greg Kroah-Hartman <gregkh@suse.de>
> Cc: linux-usb@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: Oliver Neukum <oliver@neukum.name>
> Cc: netdev@vger.kernel.org
> Cc: linux-api@vger.kernel.org
> Cc: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
> Cc: Michal Nazarewicz <mina86@mina86.com>
> Cc: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Dan Carpenter <dan.carpenter@oracle.com>
> Cc: Macpaul Lin <macpaul@gmail.com>
>
> 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);
>  }

Does that mean that new kernels will stop working with old RNDIs
gadgets because they stop recognising them as RNDIS?  I feel like this
function should accept both, i.e.:

	return (desc->bInterfaceClass == USB_CLASS_COMM &&
		desc->bInterfaceSubClass == 2 &&
		desc->bInterfaceProtocol == 0xff) ||
	       (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;
>  }

Ditto.

>  
>  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

-- 
Best regards,                                         _     _
.o. | Liege of Serenely Enlightened Majesty of      o' \,=./ `o
..o | Computer Science,  Michał “mina86” Nazarewicz    (o o)
ooo +--<mpn@google.com>--<xmpp:mina86@jabber.org>--ooO--(_)--Ooo--

^ permalink raw reply

* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Nicolas Dichtel @ 2014-09-24  9:31 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: Network Development, Linux Containers,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Stephen Hemminger, Eric W. Biederman, Linux API, Andrew Morton,
	David S. Miller
In-Reply-To: <CALCETrWnZtWstBviDxcHpLpjHF3R+sgS48RYf2pbKBYeExZVOQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

Le 23/09/2014 21:26, Andy Lutomirski a écrit :
> On Tue, Sep 23, 2014 at 6:20 AM, Nicolas Dichtel
> <nicolas.dichtel@6wind.com> wrote:
>> The goal of this serie is to be able to multicast netlink messages with an
>> attribute that identify a peer netns.
>> This is needed by the userland to interpret some informations contained in
>> netlink messages (like IFLA_LINK value, but also some other attributes in case
>> of x-netns netdevice (see also
>> http://thread.gmane.org/gmane.linux.network/315933/focus=316064 and
>> http://thread.gmane.org/gmane.linux.kernel.containers/28301/focus=4239)).
>>
>> Ids are stored in the parent user namespace. These ids are valid only inside
>> this user namespace. The user can retrieve these ids via a new netlink messages,
>> but only if peer netns are in the same user namespace.
>
> What about the parent / ancestors of the owning userns?  Can processes
> in those usernses see any form of netns id?
With this serie no. I'm not sure if ancestors really needs to be able to
get these ids. What is your opinion?
_______________________________________________
Containers mailing list
Containers@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/containers

^ permalink raw reply

* Re: [PATCH v4 1/5] drm/rockchip: Add basic drm driver
From: Mark yao @ 2014-09-24  9:31 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, linux-doc-u79uwXL29TY76Z2rM5mHXA,
	kever.yang-TNX95d0MmH7DzftRWevZcw,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	dianders-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
	zyw-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	kfx-TNX95d0MmH7DzftRWevZcw, wxt-TNX95d0MmH7DzftRWevZcw,
	huangtao-TNX95d0MmH7DzftRWevZcw,
	devicetree-u79uwXL29TY76Z2rM5mHXA, yxj-TNX95d0MmH7DzftRWevZcw,
	marcheu-F7+t8E8rja9g9hUCZPvPmw, xxm
In-Reply-To: <20140924082037.GJ15734-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org>

On 2014年09月24日 16:20, Daniel Vetter wrote:
> On Mon, Sep 22, 2014 at 06:48:54PM +0800, Mark yao wrote:
>> This patch adds the basic structure of a DRM Driver for Rockchip Socs.
>>
>> Signed-off-by: Mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> ---
>> Changes in v2:
>> - use the component framework to defer main drm driver probe
>>    until all VOP devices have been probed.
>> - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
>>    master device and each vop device can shared the drm dma mapping.
>> - use drm_crtc_init_with_planes and drm_universal_plane_init.
>> - remove unnecessary middle layers.
>> - add cursor set, move funcs to rockchip drm crtc.
>> - use vop reset at first init
>> - reference framebuffer when used and unreference when swap out vop
>>
>> Changes in v3:
>> - change "crtc->fb" to "crtc->primary-fb"
>> Adviced by Daniel Vetter
>> - init cursor plane with universal api, remove unnecessary cursor set,move
>>
>> Changes in v4:
>> Adviced by David Herrmann
>> - remove drm_platform_*() usage, use register drm device directly.
>> Adviced by Rob Clark
>> - remove special mmap ioctl, do userspace mmap with normal mmap() or mmap offset
>>
>>   drivers/gpu/drm/Kconfig                       |    2 +
>>   drivers/gpu/drm/Makefile                      |    1 +
>>   drivers/gpu/drm/rockchip/Kconfig              |   19 +
>>   drivers/gpu/drm/rockchip/Makefile             |   10 +
>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |  524 ++++++++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |  120 +++
>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c    |  201 ++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.h    |   28 +
>>   drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c |  231 +++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h |   20 +
>>   drivers/gpu/drm/rockchip/rockchip_drm_gem.c   |  404 ++++++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_gem.h   |   72 ++
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c   | 1372 +++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |  187 ++++
>>   include/uapi/drm/rockchip_drm.h               |   75 ++
> uapi is still here ... Was this an oversight?
> -Daniel
>
Hi, Daniel
this version is old, newest is v5. and I remove uapi at v5.
you can see v5 patch at:
https://lkml.org/lkml/2014/9/23/1061
thanks
-Mark
>>   15 files changed, 3266 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
>>   create mode 100644 include/uapi/drm/rockchip_drm.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..7146c80
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/Kconfig
>> @@ -0,0 +1,19 @@
>> +config DRM_ROCKCHIP
>> +	tristate "DRM Support for Rockchip"
>> +	depends on DRM && ROCKCHIP_IOMMU
>> +	select ARM_DMA_USE_IOMMU
>> +	select 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..6e6d468
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -0,0 +1,10 @@
>> +#
>> +# Makefile for the drm device driver.  This driver provides support for the
>> +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>> +
>> +ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
>> +
>> +rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_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..94926cb
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> @@ -0,0 +1,524 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> + *
>> + * 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 <drm/rockchip_drm.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_fb.h"
>> +#include "rockchip_drm_fbdev.h"
>> +#include "rockchip_drm_gem.h"
>> +
>> +#define DRIVER_NAME	"rockchip"
>> +#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_coherent_mask(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;
>> +
>> +	dev_set_drvdata(drm_dev->dev, dev);
>> +	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;
>> +	}
>> +
>> +	dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
>> +	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;
>> +	dev_set_drvdata(dev, 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;
>> +	dev_set_drvdata(dev, 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;
>> +
>> +	drm_modeset_lock_all(dev);
>> +	if (priv->fb_helper)
>> +		drm_fb_helper_restore_fbdev_mode(priv->fb_helper);
>> +	drm_modeset_unlock_all(dev);
>> +}
>> +
>> +static const struct drm_ioctl_desc rockchip_ioctls[] = {
>> +	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_gem_create_ioctl,
>> +			  DRM_UNLOCKED | DRM_AUTH),
>> +	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET, rockchip_gem_get_ioctl,
>> +			  DRM_UNLOCKED),
>> +	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
>> +			  rockchip_gem_map_offset_ioctl, DRM_UNLOCKED |
>> +			  DRM_AUTH),
>> +};
>> +
>> +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_import_sg_table	= rockchip_gem_prime_import_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");
>> +	of_node_put(np);
>> +	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_coherent_mask(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-TNX95d0MmH7DzftRWevZcw@public.gmane.org>");
>> +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-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> + *
>> + * 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..b319505
>> --- /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-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <drm/drmP.h>
>> +#include <drm/drm_fb_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <uapi/drm/rockchip_drm.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-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef _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..fe1bb22
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
>> @@ -0,0 +1,231 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_fb_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include <drm/rockchip_drm.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-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef _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..2f34e92
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
>> @@ -0,0 +1,404 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_vma_manager.h>
>> +#include <drm/rockchip_drm.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);
>> +}
>> +
>> +int rockchip_gem_get_ioctl(struct drm_device *dev, void *data,
>> +			   struct drm_file *file_priv)
>> +{
>> +	struct drm_rockchip_gem_info *args = data;
>> +	struct rockchip_gem_object *rk_obj;
>> +	struct drm_gem_object *obj;
>> +
>> +	mutex_lock(&dev->struct_mutex);
>> +
>> +	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
>> +	if (!obj) {
>> +		DRM_ERROR("failed to lookup gem object.\n");
>> +		mutex_unlock(&dev->struct_mutex);
>> +		return -EINVAL;
>> +	}
>> +
>> +	rk_obj = to_rockchip_obj(obj);
>> +
>> +	args->flags = rk_obj->flags;
>> +	args->size = obj->size;
>> +
>> +	drm_gem_object_unreference(obj);
>> +	mutex_unlock(&dev->struct_mutex);
>> +
>> +	return 0;
>> +}
>> +
>> +int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data,
>> +				  struct drm_file *file_priv)
>> +{
>> +	struct drm_rockchip_gem_map_off *args = data;
>> +
>> +	return rockchip_gem_dumb_map_offset(file_priv, drm, args->handle,
>> +					    &args->offset);
>> +}
>> +
>> +int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
>> +			      struct drm_file *file_priv)
>> +{
>> +	struct drm_rockchip_gem_create *args = data;
>> +	struct rockchip_gem_object *rk_obj;
>> +
>> +	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;
>> +}
>> +
>> +struct drm_gem_object *
>> +rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
>> +				   struct sg_table *sgt)
>> +{
>> +	struct rockchip_gem_object *rk_obj;
>> +
>> +	if (sgt->nents != 1)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	rk_obj = rockchip_gem_create_object(dev, size);
>> +	if (IS_ERR(rk_obj))
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	return &rk_obj->base;
>> +}
>> +
>> +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..6277dbd
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
>> @@ -0,0 +1,72 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef _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);
>> +int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data,
>> +				  struct drm_file *file_priv);
>> +/*
>> + * request gem object creation and buffer allocation as the size
>> + * that it is calculated with framebuffer information such as width,
>> + * height and bpp.
>> + */
>> +int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
>> +			      struct drm_file *file_priv);
>> +
>> +/* get buffer offset to map to user space. */
>> +int rockchip_gem_map_offset_ioctl(struct drm_device *dev, void *data,
>> +				  struct drm_file *file_priv);
>> +
>> +/* get buffer information to memory region allocated by gem. */
>> +int rockchip_gem_get_ioctl(struct drm_device *dev, void *data,
>> +			   struct drm_file *file_priv);
>> +#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..d2ec4d5
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -0,0 +1,1372 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +#include <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 <drm/rockchip_drm.h>
>> +
>> +#include <video/of_display_timing.h>
>> +#include <video/of_videomode.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_fbdev.h"
>> +#include "rockchip_drm_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-TNX95d0MmH7DzftRWevZcw@public.gmane.org>");
>> +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-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef _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 */
>> diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h
>> new file mode 100644
>> index 0000000..3193360
>> --- /dev/null
>> +++ b/include/uapi/drm/rockchip_drm.h
>> @@ -0,0 +1,75 @@
>> +/*
>> + *
>> + * Copyright (c) Fuzhou Rockchip Electronics Co.Ltd
>> + * Authors:
>> + *       Mark Yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> + *
>> + * base on exynos_drm.h
>> + *
>> + * This program is free software; you can redistribute  it and/or modify it
>> + * under  the terms of  the GNU General  Public License as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + */
>> +
>> +#ifndef _UAPI_ROCKCHIP_DRM_H
>> +#define _UAPI_ROCKCHIP_DRM_H
>> +
>> +#include <drm/drm.h>
>> +
>> +/**
>> + * User-desired buffer creation information structure.
>> + *
>> + * @size: user-desired memory allocation size.
>> + * @flags: user request for setting memory type or cache attributes.
>> + * @handle: returned a handle to created gem object.
>> + *     - this handle will be set by gem module of kernel side.
>> + */
>> +struct drm_rockchip_gem_create {
>> +	uint64_t size;
>> +	uint32_t flags;
>> +	uint32_t handle;
>> +};
>> +
>> +/**
>> + * A structure for getting buffer offset.
>> + *
>> + * @handle: a pointer to gem object created.
>> + * @pad: just padding to be 64-bit aligned.
>> + * @offset: relatived offset value of the memory region allocated.
>> + *     - this value should be set by user.
>> + */
>> +struct drm_rockchip_gem_map_off {
>> +	uint32_t handle;
>> +	uint32_t pad;
>> +	uint64_t offset;
>> +};
>> +
>> +/**
>> + * A structure to gem information.
>> + *
>> + * @handle: a handle to gem object created.
>> + * @flags: flag value including memory type and cache attribute and
>> + *      this value would be set by driver.
>> + * @size: size to memory region allocated by gem and this size would
>> + *      be set by driver.
>> + */
>> +struct drm_rockchip_gem_info {
>> +	uint32_t handle;
>> +	uint32_t flags;
>> +	uint64_t size;
>> +};
>> +
>> +#define DRM_ROCKCHIP_GEM_CREATE		0x00
>> +#define DRM_ROCKCHIP_GEM_GET		0x01
>> +#define DRM_ROCKCHIP_GEM_MAP_OFFSET	0x02
>> +
>> +#define DRM_IOCTL_ROCKCHIP_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + \
>> +		DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
>> +
>> +#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET	DRM_IOWR(DRM_COMMAND_BASE + \
>> +		DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
>> +
>> +#define DRM_IOCTL_ROCKCHIP_GEM_GET	DRM_IOWR(DRM_COMMAND_BASE + \
>> +		DRM_ROCKCHIP_GEM_GET, struct drm_rockchip_gem_info)
>> +#endif /* _UAPI_ROCKCHIP_DRM_H */
>> -- 
>> 1.7.9.5
>>
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel


--
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

* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Nicolas Dichtel @ 2014-09-24  9:23 UTC (permalink / raw)
  To: Cong Wang
  Cc: netdev, containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Andy Lutomirski, Stephen Hemminger, Eric W. Biederman,
	linux-api-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, David Miller
In-Reply-To: <CAHA+R7NnBJ=T3sukzzp-OD2am1nd318XbrXCX84LfSL=nu9ojw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

Le 23/09/2014 21:22, Cong Wang a écrit :
> On Tue, Sep 23, 2014 at 6:20 AM, Nicolas Dichtel
> <nicolas.dichtel@6wind.com> wrote:
>>
>> Here is a small screenshot to show how it can be used by userland:
>> $ ip netns add foo
>> $ ip netns del foo
>> $ ip netns
>> $ touch /var/run/netns/init_net
>> $ mount --bind /proc/1/ns/net /var/run/netns/init_net
>> $ ip netns add foo
>> $ ip netns
>> foo (id: 3)
>> init_net (id: 1)
>> $ ip netns exec foo ip netns
>> foo (id: 3)
>> init_net (id: 1)
>> $ ip netns exec foo ip link add ipip1 link-netnsid 1 type ipip remote 10.16.0.121 local 10.16.0.249
>> $ ip netns exec foo ip l ls ipip1
>> 6: ipip1@NONE: <POINTOPOINT,NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default
>>      link/ipip 10.16.0.249 peer 10.16.0.121 link-netnsid 1
>>
>> The parameter link-netnsid shows us where the interface sends and receives
>> packets (and thus we know where encapsulated addresses are set).
>>
>
> So ipip1 is shown in netns foo but functioning in netns init_net? Getting the
> id of init_net in foo depends on your mount namespace, /var/run/netns/ may
> not visible inside foo, in this case, link-netnsid is meaningless. It
> is not your
> fault, network namespace already heavily relies on mount namespace (sysfs
> needs to be remount otherwise you can not create device with the same name.)
>
> On the other hand, what's the problem you are trying to solve? AFAIK,
> the ifindex
> issue is purely in output, IOW, the device still functions correctly
> even through
> its link ifindex is not correct after moving to another namespace. If
> not, it is bug
> we need to fix.
>
The problem is explained here:
http://thread.gmane.org/gmane.linux.network/315933/focus=316064
and here:
http://thread.gmane.org/gmane.linux.kernel.containers/28301/focus=4239


Regards,
Nicolas
_______________________________________________
Containers mailing list
Containers@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/containers

^ permalink raw reply

* Re: [PATCH v4 1/5] drm/rockchip: Add basic drm driver
From: Daniel Vetter @ 2014-09-24  8:20 UTC (permalink / raw)
  To: Mark yao
  Cc: 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,
	linux-doc, kever.yang, dri-devel, dianders, xjq, zyw, cym,
	linux-rockchip, kfx, wxt, huangtao, devicetree, yxj, marcheu, xxm
In-Reply-To: <1411382934-1763-1-git-send-email-mark.yao@rock-chips.com>

On Mon, Sep 22, 2014 at 06:48:54PM +0800, Mark yao wrote:
> This patch adds the basic structure of a DRM Driver for Rockchip Socs.
> 
> Signed-off-by: Mark yao <mark.yao@rock-chips.com>
> ---
> Changes in v2:
> - use the component framework to defer main drm driver probe
>   until all VOP devices have been probed.
> - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
>   master device and each vop device can shared the drm dma mapping.
> - use drm_crtc_init_with_planes and drm_universal_plane_init.
> - remove unnecessary middle layers.
> - add cursor set, move funcs to rockchip drm crtc.
> - use vop reset at first init
> - reference framebuffer when used and unreference when swap out vop
> 
> Changes in v3:
> - change "crtc->fb" to "crtc->primary-fb"
> Adviced by Daniel Vetter
> - init cursor plane with universal api, remove unnecessary cursor set,move 
> 
> Changes in v4:
> Adviced by David Herrmann
> - remove drm_platform_*() usage, use register drm device directly.
> Adviced by Rob Clark
> - remove special mmap ioctl, do userspace mmap with normal mmap() or mmap offset
> 
>  drivers/gpu/drm/Kconfig                       |    2 +
>  drivers/gpu/drm/Makefile                      |    1 +
>  drivers/gpu/drm/rockchip/Kconfig              |   19 +
>  drivers/gpu/drm/rockchip/Makefile             |   10 +
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |  524 ++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |  120 +++
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.c    |  201 ++++
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.h    |   28 +
>  drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c |  231 +++++
>  drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h |   20 +
>  drivers/gpu/drm/rockchip/rockchip_drm_gem.c   |  404 ++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_gem.h   |   72 ++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c   | 1372 +++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |  187 ++++
>  include/uapi/drm/rockchip_drm.h               |   75 ++

uapi is still here ... Was this an oversight?
-Daniel

>  15 files changed, 3266 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
>  create mode 100644 include/uapi/drm/rockchip_drm.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..7146c80
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -0,0 +1,19 @@
> +config DRM_ROCKCHIP
> +	tristate "DRM Support for Rockchip"
> +	depends on DRM && ROCKCHIP_IOMMU
> +	select ARM_DMA_USE_IOMMU
> +	select 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..6e6d468
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -0,0 +1,10 @@
> +#
> +# Makefile for the drm device driver.  This driver provides support for the
> +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
> +
> +ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
> +
> +rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_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..94926cb
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -0,0 +1,524 @@
> +/*
> + * 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 <drm/rockchip_drm.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_fbdev.h"
> +#include "rockchip_drm_gem.h"
> +
> +#define DRIVER_NAME	"rockchip"
> +#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_coherent_mask(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;
> +
> +	dev_set_drvdata(drm_dev->dev, dev);
> +	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;
> +	}
> +
> +	dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
> +	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;
> +	dev_set_drvdata(dev, 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;
> +	dev_set_drvdata(dev, 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;
> +
> +	drm_modeset_lock_all(dev);
> +	if (priv->fb_helper)
> +		drm_fb_helper_restore_fbdev_mode(priv->fb_helper);
> +	drm_modeset_unlock_all(dev);
> +}
> +
> +static const struct drm_ioctl_desc rockchip_ioctls[] = {
> +	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_gem_create_ioctl,
> +			  DRM_UNLOCKED | DRM_AUTH),
> +	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET, rockchip_gem_get_ioctl,
> +			  DRM_UNLOCKED),
> +	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
> +			  rockchip_gem_map_offset_ioctl, DRM_UNLOCKED |
> +			  DRM_AUTH),
> +};
> +
> +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_import_sg_table	= rockchip_gem_prime_import_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");
> +	of_node_put(np);
> +	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_coherent_mask(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..b319505
> --- /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/drmP.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <uapi/drm/rockchip_drm.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..fe1bb22
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
> @@ -0,0 +1,231 @@
> +/*
> + * 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/drmP.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include <drm/rockchip_drm.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..2f34e92
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
> @@ -0,0 +1,404 @@
> +/*
> + * 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/drmP.h>
> +#include <drm/drm_vma_manager.h>
> +#include <drm/rockchip_drm.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);
> +}
> +
> +int rockchip_gem_get_ioctl(struct drm_device *dev, void *data,
> +			   struct drm_file *file_priv)
> +{
> +	struct drm_rockchip_gem_info *args = data;
> +	struct rockchip_gem_object *rk_obj;
> +	struct drm_gem_object *obj;
> +
> +	mutex_lock(&dev->struct_mutex);
> +
> +	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
> +	if (!obj) {
> +		DRM_ERROR("failed to lookup gem object.\n");
> +		mutex_unlock(&dev->struct_mutex);
> +		return -EINVAL;
> +	}
> +
> +	rk_obj = to_rockchip_obj(obj);
> +
> +	args->flags = rk_obj->flags;
> +	args->size = obj->size;
> +
> +	drm_gem_object_unreference(obj);
> +	mutex_unlock(&dev->struct_mutex);
> +
> +	return 0;
> +}
> +
> +int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data,
> +				  struct drm_file *file_priv)
> +{
> +	struct drm_rockchip_gem_map_off *args = data;
> +
> +	return rockchip_gem_dumb_map_offset(file_priv, drm, args->handle,
> +					    &args->offset);
> +}
> +
> +int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
> +			      struct drm_file *file_priv)
> +{
> +	struct drm_rockchip_gem_create *args = data;
> +	struct rockchip_gem_object *rk_obj;
> +
> +	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;
> +}
> +
> +struct drm_gem_object *
> +rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
> +				   struct sg_table *sgt)
> +{
> +	struct rockchip_gem_object *rk_obj;
> +
> +	if (sgt->nents != 1)
> +		return ERR_PTR(-EINVAL);
> +
> +	rk_obj = rockchip_gem_create_object(dev, size);
> +	if (IS_ERR(rk_obj))
> +		return ERR_PTR(-ENOMEM);
> +
> +	return &rk_obj->base;
> +}
> +
> +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..6277dbd
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
> @@ -0,0 +1,72 @@
> +/*
> + * 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);
> +int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data,
> +				  struct drm_file *file_priv);
> +/*
> + * request gem object creation and buffer allocation as the size
> + * that it is calculated with framebuffer information such as width,
> + * height and bpp.
> + */
> +int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
> +			      struct drm_file *file_priv);
> +
> +/* get buffer offset to map to user space. */
> +int rockchip_gem_map_offset_ioctl(struct drm_device *dev, void *data,
> +				  struct drm_file *file_priv);
> +
> +/* get buffer information to memory region allocated by gem. */
> +int rockchip_gem_get_ioctl(struct drm_device *dev, void *data,
> +			   struct drm_file *file_priv);
> +#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..d2ec4d5
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -0,0 +1,1372 @@
> +/*
> + * 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/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 <drm/rockchip_drm.h>
> +
> +#include <video/of_display_timing.h>
> +#include <video/of_videomode.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_fbdev.h"
> +#include "rockchip_drm_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 */
> diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h
> new file mode 100644
> index 0000000..3193360
> --- /dev/null
> +++ b/include/uapi/drm/rockchip_drm.h
> @@ -0,0 +1,75 @@
> +/*
> + *
> + * Copyright (c) Fuzhou Rockchip Electronics Co.Ltd
> + * Authors:
> + *       Mark Yao <yzq@rock-chips.com>
> + *
> + * base on exynos_drm.h
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#ifndef _UAPI_ROCKCHIP_DRM_H
> +#define _UAPI_ROCKCHIP_DRM_H
> +
> +#include <drm/drm.h>
> +
> +/**
> + * User-desired buffer creation information structure.
> + *
> + * @size: user-desired memory allocation size.
> + * @flags: user request for setting memory type or cache attributes.
> + * @handle: returned a handle to created gem object.
> + *     - this handle will be set by gem module of kernel side.
> + */
> +struct drm_rockchip_gem_create {
> +	uint64_t size;
> +	uint32_t flags;
> +	uint32_t handle;
> +};
> +
> +/**
> + * A structure for getting buffer offset.
> + *
> + * @handle: a pointer to gem object created.
> + * @pad: just padding to be 64-bit aligned.
> + * @offset: relatived offset value of the memory region allocated.
> + *     - this value should be set by user.
> + */
> +struct drm_rockchip_gem_map_off {
> +	uint32_t handle;
> +	uint32_t pad;
> +	uint64_t offset;
> +};
> +
> +/**
> + * A structure to gem information.
> + *
> + * @handle: a handle to gem object created.
> + * @flags: flag value including memory type and cache attribute and
> + *      this value would be set by driver.
> + * @size: size to memory region allocated by gem and this size would
> + *      be set by driver.
> + */
> +struct drm_rockchip_gem_info {
> +	uint32_t handle;
> +	uint32_t flags;
> +	uint64_t size;
> +};
> +
> +#define DRM_ROCKCHIP_GEM_CREATE		0x00
> +#define DRM_ROCKCHIP_GEM_GET		0x01
> +#define DRM_ROCKCHIP_GEM_MAP_OFFSET	0x02
> +
> +#define DRM_IOCTL_ROCKCHIP_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + \
> +		DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
> +
> +#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET	DRM_IOWR(DRM_COMMAND_BASE + \
> +		DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
> +
> +#define DRM_IOCTL_ROCKCHIP_GEM_GET	DRM_IOWR(DRM_COMMAND_BASE + \
> +		DRM_ROCKCHIP_GEM_GET, struct drm_rockchip_gem_info)
> +#endif /* _UAPI_ROCKCHIP_DRM_H */
> -- 
> 1.7.9.5
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply

* Re: [PATCH v2 1/7] selftests: add kselftest framework for uniform test reporting
From: Davidlohr Bueso @ 2014-09-24  8:09 UTC (permalink / raw)
  To: Shuah Khan
  Cc: akpm, gregkh, colin.king, ebiederm, serge.hallyn, thierry, luto,
	linux-api, linux-kernel
In-Reply-To: <0b1961bd4d2104beafd2e8c1a17690f6efe14a61.1411506121.git.shuahkh@osg.samsung.com>

On Tue, 2014-09-23 at 15:32 -0600, Shuah Khan wrote:
> 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@osg.samsung.com>
> ---
>  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@osg.samsung.com>
> + * 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;

unsigned int?

> +static inline void ksft_inc_pass_cnt(void) { ksft_pass++; }
> +static inline void ksft_inc_fail_cnt(void) { ksft_fail++; }
> +static inline void ksft_inc_xfail_cnt(void) { ksft_xfail++; }
> +static inline void ksft_inc_xpass_cnt(void) { ksft_xpass++; }
> +static inline void ksft_inc_xskip_cnt(void) { ksft_xskip++; }

It would probably make sense to have the counters in a structures,
something like: struct ksft_counter { ... } ksft_cnt;

Then just pass it around the proposed functions as arguments. That also
minimizes a bit the global variables and would allow you to easily
change it in the future.

> +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);
> +}

Same here, just do:

static inline void ksft_print_cnts(struct ksft_counter counters)
{ 
	... 
}

Otherwise looks good.

> +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 */

^ permalink raw reply

* Re: [PATCH 2/2] perf: Userspace software event and ioctl
From: Ingo Molnar @ 2014-09-24  7:49 UTC (permalink / raw)
  To: Pawel Moll
  Cc: Ingo Molnar, Arnaldo Carvalho de Melo, Richard Cochran,
	Steven Rostedt, Peter Zijlstra, Paul Mackerras, John Stultz,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <1411491764.3922.46.camel@hornet>


* Pawel Moll <pawel.moll-5wv7dgnIgG8@public.gmane.org> wrote:

> On Thu, 2014-09-18 at 15:34 +0100, Pawel Moll wrote:
> > This patch adds a PERF_COUNT_SW_USERSPACE_EVENT type,
> > which can be generated by user with PERF_EVENT_IOC_ENTRY
> > ioctl command, which injects an event of said type into
> > the perf buffer.
> 
> It occurred to me last night that currently perf doesn't handle "write"
> syscall at all, while this seems like the most natural way of
> "injecting" userspace events into perf buffer.
> 
> An ioctl would still be needed to set a type of the following events,
> something like:
> 
> 	ioctl(SET_TYPE, 0x42);
> 	write(perf_fd, binaryblob, size);
> 	ioctl(SET_TYPE, 0);
> 	dprintf(perf_fd, "String");
> 
> which is fine for use cases when the type doesn't change often, 
> but would double the amount of syscalls when every single event 
> is of a different type. Perhaps there still should be a 
> "generating ioctl" taking both type and data/size in one go?

Absolutely, there should be a single syscall.

I'd even argue it should be a new prctl(): that way we could both 
generate user events for specific perf fds, but also into any 
currently active context (that allows just generation/injection 
of user events). In the latter case we might have no fd to work 
off from.

And that is actually the really exciting usecase of your patches: 
we could generate user events via simple commands, and any 
external profiler/trace would be able to see them.

Thanks,

	Ingo

^ permalink raw reply

* Re: [PATCH v2 2/2] perf: Userspace event
From: Ingo Molnar @ 2014-09-24  7:20 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Pawel Moll, 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: <87oau5k0u9.fsf-vfBCOVm4yAnB69T4xOojN9BPR1lH4CV8@public.gmane.org>


* Namhyung Kim <namhyung-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:

> 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.

It would also be nice to add support for this to tools/perf/ (so 
that 'trace' displays such entries in a perf.data), with a 
minimum testcase for 'perf test' as well.

Perhaps also add a small sub-utility to inject such events from 
the command line, such as:

  trace user-event "this is a test message"

('trace' is a shortcut command for 'perf trace'.)

It would have a usecase straight away: perf could be used to 
easily trace script execution for example.

For that probably another mode of user event generation would be 
needed as well: a process that has no access to any perf fds 
should still be able to generate user events, if the 
profiling/tracing context has permitted that. In this case we'd 
inject the event either into the first, or all currently active 
events (but only once per output buffer, or so).

Thanks,

	Ingo

^ permalink raw reply

* [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


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